From e6f76e66a0b42c425e4f939e094b6b61c8d3413e Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 12:08:33 +0000 Subject: [PATCH 1/3] chore: pin effect packages to npm 4.0.2 Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- package.json | 6 +- packages/cli/package.json | 4 +- packages/e2e/package.json | 8 +- packages/effect-app/package.json | 2 +- packages/infra/package.json | 6 +- packages/vue-components/package.json | 2 +- packages/vue/package.json | 8 +- pnpm-lock.yaml | 148 +++++++++++++-------------- pnpm-workspace.yaml | 14 +-- 9 files changed, 99 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 90f2cb16e..06659be2e 100644 --- a/package.json +++ b/package.json @@ -50,15 +50,15 @@ "@effect-app/infra": "workspace:*", "@effect/language-service": "0.87.3", "@effect/tsgo": "^0.47.2", - "@effect/platform-node": "4.0.1", - "@effect/vitest": "4.0.1", + "@effect/platform-node": "4.0.2", + "@effect/vitest": "4.0.2", "@tsconfig/strictest": "^2.0.8", "@types/node": "25.9.1", "@typescript-eslint/eslint-plugin": "8.60.0", "@typescript-eslint/parser": "8.60.0", "@typescript/native-preview": "7.0.0-dev.20260626.1", "dprint": "^0.54.0", - "effect": "4.0.1", + "effect": "4.0.2", "effect-app": "workspace:*", "eslint": "^10.4.1", "json5": "^2.2.3", diff --git a/packages/cli/package.json b/packages/cli/package.json index d82334393..2f6f6d1c7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -14,8 +14,8 @@ "effect-app-cli": "./bin.js" }, "dependencies": { - "@effect/platform-node": "4.0.1", - "effect": "4.0.1", + "@effect/platform-node": "4.0.2", + "effect": "4.0.2", "js-yaml": "4.2.0" }, "devDependencies": { diff --git a/packages/e2e/package.json b/packages/e2e/package.json index 59035e517..87f8674be 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -10,13 +10,13 @@ "effect-app": "workspace:*" }, "devDependencies": { - "@effect/atom-vue": "^4.0.1", - "@effect/platform-node": "4.0.1", - "@effect/vitest": "4.0.1", + "@effect/atom-vue": "^4.0.2", + "@effect/platform-node": "4.0.2", + "@effect/vitest": "4.0.2", "@tanstack/vue-query": "5.96.2", "@types/node": "25.9.1", "@vitejs/plugin-vue": "^6.0.7", - "effect": "^4.0.1", + "effect": "^4.0.2", "typescript": "~6.0.3", "vitest": "^4.1.7", "vue": "^3.5.35" diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json index 1a902a0aa..aebfb050b 100644 --- a/packages/effect-app/package.json +++ b/packages/effect-app/package.json @@ -25,7 +25,7 @@ "vitest": "^4.1.7" }, "peerDependencies": { - "effect": "^4.0.1" + "effect": "^4.0.2" }, "typesVersions": { "*": { diff --git a/packages/infra/package.json b/packages/infra/package.json index 9e3870fe8..9b7b15307 100644 --- a/packages/infra/package.json +++ b/packages/infra/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@azure/cosmos": "^4.9.3", "@azure/service-bus": "^7.9.5", - "@effect/sql-sqlite-node": "4.0.1", + "@effect/sql-sqlite-node": "4.0.2", "@sentry/opentelemetry": "10.55.0", "@types/better-sqlite3": "^7.6.13", "@types/node": "25.9.1", @@ -39,12 +39,12 @@ "peerDependencies": { "@azure/cosmos": "^4.9.3", "@azure/service-bus": "^7.9.5", - "@effect/vitest": "^4.0.1", + "@effect/vitest": "^4.0.2", "@sendgrid/helpers": "^8.0.0", "@sendgrid/mail": "^8.1.6", "@sentry/node": "10.55.0", "@sentry/opentelemetry": "10.55.0", - "effect": "^4.0.1", + "effect": "^4.0.2", "jwt-decode": "^4.0.0", "redis": "^3.1.2", "redlock": "^4.2.0" diff --git a/packages/vue-components/package.json b/packages/vue-components/package.json index 18c837d47..5ec2ca716 100644 --- a/packages/vue-components/package.json +++ b/packages/vue-components/package.json @@ -25,7 +25,7 @@ }, "peerDependencies": { "@mdi/js": "^7.4.47", - "effect": "^4.0.1", + "effect": "^4.0.2", "intl-messageformat": "^11.2.7", "mdi-js": "^1.0.1", "primeflex": "^4.0.0", diff --git a/packages/vue/package.json b/packages/vue/package.json index 2625f3ca5..c02bf5c16 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -18,14 +18,14 @@ "query-string": "^9.4.0" }, "peerDependencies": { - "@effect/atom-vue": "^4.0.1", - "@effect/platform-browser": "^4.0.1", + "@effect/atom-vue": "^4.0.2", + "@effect/platform-browser": "^4.0.2", "@sentry/browser": "^10.55.0", - "effect": "^4.0.1", + "effect": "^4.0.2", "vue": "^3.5.35" }, "devDependencies": { - "@effect/vitest": "4.0.1", + "@effect/vitest": "4.0.2", "@formatjs/icu-messageformat-parser": "^3.5.10", "@types/node": "25.9.1", "@vitejs/plugin-vue": "^6.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0079d4d2c..a587bc4e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,14 +42,14 @@ importers: specifier: 0.87.3 version: 0.87.3 "@effect/platform-node": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/tsgo": specifier: ^0.47.2 version: 0.47.2 "@effect/vitest": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@tsconfig/strictest": specifier: ^2.0.8 version: 2.0.8 @@ -69,8 +69,8 @@ importers: specifier: ^0.54.0 version: 0.54.0 effect: - specifier: 4.0.1 - version: 4.0.1 + specifier: 4.0.2 + version: 4.0.2 effect-app: specifier: workspace:* version: link:packages/effect-app @@ -114,11 +114,11 @@ importers: packages/cli: dependencies: "@effect/platform-node": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) effect: - specifier: 4.0.1 - version: 4.0.1 + specifier: 4.0.2 + version: 4.0.2 js-yaml: specifier: 4.2.0 version: 4.2.0 @@ -150,14 +150,14 @@ importers: version: link:../effect-app devDependencies: "@effect/atom-vue": - specifier: ^4.0.1 - version: 4.0.1(effect@4.0.1)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) + specifier: ^4.0.2 + version: 4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) "@effect/platform-node": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/vitest": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@tanstack/vue-query": specifier: 5.96.2 version: 5.96.2(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) @@ -168,8 +168,8 @@ importers: specifier: ^6.0.7 version: 6.0.7(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) effect: - specifier: ^4.0.1 - version: 4.0.1 + specifier: ^4.0.2 + version: 4.0.2 typescript: specifier: ~6.0.3 version: 6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e) @@ -186,8 +186,8 @@ importers: specifier: ^2.0.8 version: 2.0.8 effect: - specifier: ^4.0.1 - version: 4.0.1 + specifier: ^4.0.2 + version: 4.0.2 nanoid: specifier: ^5.1.11 version: 5.1.11 @@ -325,8 +325,8 @@ importers: packages/infra: dependencies: "@effect/vitest": - specifier: ^4.0.1 - version: 4.0.1(effect@4.0.1)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: ^4.0.2 + version: 4.0.2(effect@4.0.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@faker-js/faker": specifier: ^8.4.1 version: 8.4.1 @@ -340,8 +340,8 @@ importers: specifier: 10.55.0 version: 10.55.0(supports-color@8.1.1) effect: - specifier: ^4.0.1 - version: 4.0.1 + specifier: ^4.0.2 + version: 4.0.2 effect-app: specifier: workspace:* version: link:../effect-app @@ -368,8 +368,8 @@ importers: specifier: ^7.9.5 version: 7.9.5(supports-color@8.1.1) "@effect/sql-sqlite-node": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2) "@sentry/opentelemetry": specifier: 10.55.0 version: 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) @@ -414,11 +414,11 @@ importers: packages/vue: dependencies: "@effect/atom-vue": - specifier: ^4.0.1 - version: 4.0.1(effect@4.0.1)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) + specifier: ^4.0.2 + version: 4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) "@effect/platform-browser": - specifier: ^4.0.1 - version: 4.0.1(effect@4.0.1) + specifier: ^4.0.2 + version: 4.0.2(effect@4.0.2) "@formatjs/intl": specifier: ^4.1.12 version: 4.1.12 @@ -435,8 +435,8 @@ importers: specifier: ^5.4.4 version: 5.4.4 effect: - specifier: ^4.0.1 - version: 4.0.1 + specifier: ^4.0.2 + version: 4.0.2 effect-app: specifier: workspace:* version: link:../effect-app @@ -448,8 +448,8 @@ importers: version: 3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)) devDependencies: "@effect/vitest": - specifier: 4.0.1 - version: 4.0.1(effect@4.0.1)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.2 + version: 4.0.2(effect@4.0.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@formatjs/icu-messageformat-parser": specifier: ^3.5.10 version: 3.5.10 @@ -488,8 +488,8 @@ importers: specifier: ^1.32.0 version: 1.32.0(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) effect: - specifier: ^4.0.1 - version: 4.0.1 + specifier: ^4.0.2 + version: 4.0.2 effect-app: specifier: workspace:* version: link:../effect-app @@ -1176,12 +1176,12 @@ packages: cpu: [x64] os: [win32] - "@effect/atom-vue@4.0.1": + "@effect/atom-vue@4.0.2": resolution: { - integrity: sha512-pYSTQv0/W/UkZasvV51B5MmNfYmkWJALUBsbVAHd0tNqALRAE3K5M6wGks7N29gnl6swQz3UkMYX9XWqHiMcCg==, + integrity: sha512-ftfF/0tHKEVHsT+K70nfZehcB10Y9I5AyvWSKSc3DqOonUf6y65SGT4+bhlp1gitfo8fJpXRRta+nH+KpK7ppg==, } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 vue: ^3.5.35 "@effect/language-service@0.87.3": @@ -1190,36 +1190,36 @@ packages: } hasBin: true - "@effect/platform-browser@4.0.1": + "@effect/platform-browser@4.0.2": resolution: { - integrity: sha512-RsnobWEnffdkRH/wZ3ARIAgMxtGLyfrD0R3iVS+dwQCdYrSbotlJHEoVHEgzcOn5RzMsHG6FHkfdgRMwvcM7kg==, + integrity: sha512-juVpWmx+2e5pw3MsMSMzwsQ7sut3exkeweaQToAcn+4PKGNWvItZ72+qAalgRNQE5luPiodP507oe44gC36Vsg==, } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 - "@effect/platform-node-shared@4.0.1": + "@effect/platform-node-shared@4.0.2": resolution: { - integrity: sha512-VanaejudEhKJ4mpmqnEzdncVUhN6sGSO9ZDND54mRxAlZxj+4dYAWjDTYAy9o4tJo9aALeP2U8cVcOFzEdSeLg==, + integrity: sha512-xn8aDw9p/QUPt1Yi0C3HUMcycT+MjHfAe19FDUDrUuzcwq0xxskkamVSqbdykF/Dsuus4aZTsCBZA/vkhHIfbQ==, } engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 - "@effect/platform-node@4.0.1": + "@effect/platform-node@4.0.2": resolution: { - integrity: sha512-FT0Fu5LO0/XP9zBpj0HqrtJNLluK4StNKqf6TSulUsjp8FPBsDVXGJiDuyoN5J2yVMT7tgvhxSwaDcObWVRIaQ==, + integrity: sha512-RWesXBQStnFHdD465gR5thFKgXO7FSyCKRZ56nWXB7PedNVzr8vV0nb/3MULLdri493sMz/b/pmGBTb6K8cBjQ==, } engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 redis: ">=5.0.0 <7.0.0" - "@effect/sql-sqlite-node@4.0.1": + "@effect/sql-sqlite-node@4.0.2": resolution: { - integrity: sha512-VBlNvfFRxyyiu1viNygxA0ZVGZcpxBHA3+QLobaLQSXgAP0submgo98IzioWTHzdZUc9Xodi7ICci5ehhbD2fQ==, + integrity: sha512-QjhQ6yetczGE08ZV/05Y4Cj7GNGswloRZuk2cZ4tl84C91398Ho0PhDLexihO2RHBTE1YjpUW8Mdl4KH41I/LQ==, } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 "@effect/tsgo-darwin-arm64@0.47.2": resolution: { @@ -1276,12 +1276,12 @@ packages: } hasBin: true - "@effect/vitest@4.0.1": + "@effect/vitest@4.0.2": resolution: { - integrity: sha512-DbY9kf/018zBxRIX7rJwBnPVPzSarnUpcshm0vfYPFiDkSa7AFrXTh2BlDNUbrdrmqcIOBhVZ7GkEtxf8WiQrg==, + integrity: sha512-Zb72nu7iUy3rNdBp9J1Xu5oUQGwJ8cOYsUwmYgujj88aqyvASRDjOS01SjlcPViqf77csduetKdCJTVndMNfzw==, } peerDependencies: - effect: ^4.0.1 + effect: ^4.0.2 vitest: ">=5.0.0 <6.0.0" "@emnapi/core@1.10.0": @@ -4099,9 +4099,9 @@ packages: integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==, } - "@types/ws@8.18.1": + "@types/ws@8.18.2": resolution: { - integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, + integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==, } "@typescript-eslint/eslint-plugin@8.60.0": @@ -5647,9 +5647,9 @@ packages: engines: { node: ">=14" } hasBin: true - effect@4.0.1: + effect@4.0.2: resolution: { - integrity: sha512-b1VlQG9g8fwxE5QnIZuPoOP/0MsqHJSRKxUijjoM80E5q95FXrX5yWtY5XI8G+GJsBQAVSDbLS458z9a++uVvw==, + integrity: sha512-YeYnLqSFrL4+tCOXRrfNP03w8FILoF/wDl+ukraJAGzXrjcS383W7LAAvGBbLatEyktmLxRq+9xJinx7QzXW5A==, } electron-to-chromium@1.5.267: @@ -10133,39 +10133,39 @@ snapshots: "@dprint/win32-x64@0.54.0": optional: true - "@effect/atom-vue@4.0.1(effect@4.0.1)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)))": + "@effect/atom-vue@4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)))": dependencies: - effect: 4.0.1 + effect: 4.0.2 vue: 3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)) "@effect/language-service@0.87.3": {} - "@effect/platform-browser@4.0.1(effect@4.0.1)": + "@effect/platform-browser@4.0.2(effect@4.0.2)": dependencies: - effect: 4.0.1 + effect: 4.0.2 - "@effect/platform-node-shared@4.0.1(effect@4.0.1)": + "@effect/platform-node-shared@4.0.2(effect@4.0.2)": dependencies: - "@types/ws": 8.18.1 - effect: 4.0.1 + "@types/ws": 8.18.2 + effect: 4.0.2 ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate - "@effect/platform-node@4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.1))": + "@effect/platform-node@4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1))": dependencies: - "@effect/platform-node-shared": 4.0.1(effect@4.0.1) - effect: 4.0.1 + "@effect/platform-node-shared": 4.0.2(effect@4.0.2) + effect: 4.0.2 redis: 6.2.1(@opentelemetry/api@1.9.1) undici: 8.11.2 transitivePeerDependencies: - bufferutil - utf-8-validate - "@effect/sql-sqlite-node@4.0.1(effect@4.0.1)": + "@effect/sql-sqlite-node@4.0.2(effect@4.0.2)": dependencies: - effect: 4.0.1 + effect: 4.0.2 "@effect/tsgo-darwin-arm64@0.47.2": optional: true @@ -10198,9 +10198,9 @@ snapshots: "@effect/tsgo-win32-arm64": 0.47.2 "@effect/tsgo-win32-x64": 0.47.2 - "@effect/vitest@4.0.1(effect@4.0.1)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)))": + "@effect/vitest@4.0.2(effect@4.0.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)))": dependencies: - effect: 4.0.1 + effect: 4.0.2 vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)) "@emnapi/core@1.10.0": @@ -11618,7 +11618,7 @@ snapshots: dependencies: "@types/webidl-conversions": 7.0.3 - "@types/ws@8.18.1": + "@types/ws@8.18.2": dependencies: "@types/node": 25.9.1 @@ -12809,7 +12809,7 @@ snapshots: minimatch: 9.0.1 semver: 7.7.3 - effect@4.0.1: {} + effect@4.0.2: {} electron-to-chromium@1.5.267: optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0ac4f8615..b83575b6a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,13 +17,13 @@ minimumReleaseAgeExclude: - "@effect/tsgo-win32-x64@0.47.2" - "@effect/tsgo@0.47.2" - "@effect/language-service@0.87.3" - - "@effect/atom-vue@4.0.1" - - "@effect/platform-browser@4.0.1" - - "@effect/platform-node-shared@4.0.1" - - "@effect/platform-node@4.0.1" - - "@effect/sql-sqlite-node@4.0.1" - - "@effect/vitest@4.0.1" - - effect@4.0.1 + - "@effect/atom-vue@4.0.2" + - "@effect/platform-browser@4.0.2" + - "@effect/platform-node-shared@4.0.2" + - "@effect/platform-node@4.0.2" + - "@effect/sql-sqlite-node@4.0.2" + - "@effect/vitest@4.0.2" + - effect@4.0.2 overrides: date-fns: ^4.4.0 fast-check: ^4.9.0 From 80e0bfed78fe0b457830b7253b274fed939cc9b1 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 12:08:50 +0000 Subject: [PATCH 2/3] Squashed 'repos/effect/' changes from 460272d304..269a7c8643 269a7c8643 Version Packages (#8744) 757821fe99 Fix PostgreSQL cancellation test readiness and cleanup (#8879) 540ecd1a94 Require explicit @stability on every module and importable export (#8770) 1b35732952 Fix Stream.slidingSize dropping elements from the final partial window (#8878) 7a721cee6b Keep `=` inside values when decoding key-value config strings (#8877) 78994c5e0d docs(http-api): fix stale HttpApiBuilder.middlewareSecurity references (#8876) 0aeb7fd6f5 Preserve active OpenTelemetry parents for Effect spans (#8586) d350f8e7ba Fix HttpApi form and GET payloads rejecting single-value arrays (#8858) f6af4b6d24 expose more of the atom registry apis (#8875) 00f1cc783f fix(sql): follow OpenTelemetry database semantic conventions in SQL spans (#8866) 146e86a689 fix(http): align HTTP tracing with OpenTelemetry semantic conventions (#8873) f5716ad55d fix(observability): align OTLP exporter, resource and logger with the OpenTelemetry spec (#8868) a0e1b871b4 fix(tracing): leave interrupted spans Unset and drop synthetic stack frames (#8864) 26c108919f fix(ai): follow the OpenTelemetry GenAI semantic conventions (#8870) c6086ee5a2 fix(rpc): set span kind and rpc.* attributes per OpenTelemetry RPC conventions (#8867) 26931d0fee fix(observability): export OTLP Summary metrics and UCUM units (#8869) 763b445f55 Fix SQL event-log notifications before transaction commit (#8855) 7ccfeb3569 fix(sql-pg): report a stale plan inside a transaction instead of retrying it (#8861) d4f4bc8b83 Fix Stream.groupBy and partition hanging when a substream stops early (#8857) 2131d44bd1 fix(sql-pg): release connection listeners and retirement hooks on scope closure (#8854) ec540703a5 Update repository dependencies (#8740) 7d8d4979c6 Fix logged status after successful WebSocket upgrades (#8852) 48a373b2b4 Fix NodeHttpServer keep-alive requests during graceful shutdown (#8846) bc4d76e44a Add trusted /effect-bot maintainer comment relay (#8853) 6c59de782b Preserve defects and interruption in Arbitrary mixed causes (#8849) eee0eeb1e4 Fix suspended compiled Struct parser output reuse (#8810) 5630677ff5 fix(ai): preserve MCP toolkit service requirements (#8842) 972c80fb02 Fix SqlEventJournal on PostgreSQL (#8756) 053e3ead5a Keep atom-react server snapshots stable for delayed Suspense hydration (#8841) 22d6f4dcd4 Mark HttpMiddleware.tracer spans as failed for 5xx responses (#8821) 0ac6ac813c Fix RPC request defect serialization for SchemaBinary (#8835) 42763ce0b4 Use the negotiated MCP protocol version when the header is absent (#8775) 7234786687 Fix K8sHttpClient service-account token rotation (#8836) fb757e3eae Fix Cache.get startup interruption race (#8837) af114e664b Fix Channel.decodeText options for Node-only declarations (#8838) bd96be0f1c Fix HttpApi response schema error status and reporting (#8834) e36abbba72 Fix empty framed RPC HTTP responses (#8833) 375c1b021c Keep the URL hash and drop an empty query in Atom.searchParam (#8820) 46b75b1a7c Fix YAML block scalar trailing line breaks (#8752) a0cb4c25d8 ai: let decisions carry images (#8832) 7095539c23 Fix TOML multiline strings keeping a leading CRLF (#8753) 45a70ced8c Make Atom.windowFocusSignal work when window is undefined (#8771) 2b3c63c14f Fix HttpClient retaining unread responses without FinalizationRegistry (#8830) 7064da92f2 Fix Channel.runIntoPubSub error type (#8757) c6f2179d3d Fix interruption while waiting for transactional permits and locks (#8805) b9d6581931 Fix Config parsing to preserve composite provider causes (#8808) 3ed997b1f4 Fix Channel.merge propagation of side finalizer failures (#8816) 2fbecc82c3 Fix schedule drivers discarding finalizer defects (#8799) aefe05aea1 Fix Map/Set comparison builders accepting proper subsets as equal (#8814) 82303c1bc8 Fix ManagedRuntime disposal from managed effects (#8783) 88917ebb2e Fix formatJson redaction for getter-returned values with toJSON (#8782) 985860df71 Fix transaction retries discarding cleanup failures (#8780) 76fcd898c5 Fix ScopedCache pending entries after expired finalizer failure (#8803) c07c144b1a Reproduce: Cron.next returns a past occurrence during the second DST fold hour (#8800) c419a0edaa Reproduce: PartitionedSemaphore synchronously resumes permit chains until the JavaScript stack overflows (#8789) 99b6b45aff Reproduce: Mutating an empty concatenation result contaminates every array reducer (#8815) 684ccdc20b Fix batched logger shutdown ordering and interrupted output (#8807) 8903b68add Fix child fibers reusing completed transaction journals (#8777) b79fcb8b62 Fix PubSub surplus publishing after end (#8786) 3cb395e151 Fix ExecutionPlan schedule error and requirement tracking (#8765) c381fbc045 Fix stack overflow when completing coalesced cached requests (#8804) f31e6b7fd2 Fix Stream.tapSink completion during an upstream pull (#8793) b817ebefc0 Fix suspended union parsers reusing state across effect runs (#8809) 7b396bd3b5 Fix mergeAll discarding inner scope finalizer defects (#8798) 8409eb4592 Fix Floyd-Warshall path reconstruction through zero-cost cycles (#8813) 2e03688cdf fix(Stream): preserve sink leftovers when peeling (#8792) 716e358e0e Reproduce: Repeated throwing cause handlers overflow the interpreter stack and bypass finalization (#8778) a6c7dcb042 Fix one-shot iterable inputs lost on transaction retries (#8806) 87813882fa Fix Channel callback registration failure propagation (#8817) e4cb9f1f58 Fix inherited Standard Schema adapters bypassing child schemas (#8811) 888a18915c Reject fractional and NaN PubSub capacities (#8790) f11526aea2 Fix tapSink swallowing sink failures after end-of-stream (#8794) 7e7ede67d8 Fix Queue capacity release after reentrant shutdown (#8785) 33303ea634 Fix Pool shutdown to wait for borrowed resources (#8788) 5326527918 Fix Channel.acquireUseRelease resource leak on acquisition interruption (#8796) c5cf8ce402 Fix resource lifetime when retrying Layer.buildWithMemoMap (#8781) d85a3095af Fix Queue.clear swallowing failures combined with Done (#8791) 332d363f07 Fix Sink.make successful pipeline results (#8797) 495740d9d0 Reproduce: zipLatest hangs when one finite input is empty and another emits a value (#8795) efebfa9cf9 Include completion services in McpServer prompt requirements (#8762) 9b8200da0f Reproduce: PubSub takers can observe the final message before data published during waiter registration (#8787) 18035009c9 Fix interruption masking before scope finalizer callbacks (#8779) a84aa4e863 Fix FiberHandle ownership during reentrant replacement (#8784) 166437bdb8 Omit cumulative min/max from delta OTLP histogram points (#8769) 98a5ed7603 Fix HttpApiClient result type for union response modes (#8767) 50a5686ab6 Fix effectify error mapper exceptions stranding fibers (#8776) d0b3862b65 Fix Channel.mapInput data-last overload dropping requirements (#8758) 1ba8521582 Fix Sink.catchCause data-last error type (#8764) 44defad43f Fix HttpServer service serve middleware service requirements (#8759) 467d3aceff Include runtime error in AtomRuntime.subscriptionRef result type (#8760) a3b4cf3e2b fix(queue): deliver taken values before scheduler yields (#8819) 90a1bae898 Make RPC socket ping timing configurable and count all server frames as liveness (#8825) 1f74b62b6c feat(ai): let McpServer.layerHttp terminate sessions on DELETE (#8773) fce9c52cab Document typed SQLite setup failures in migration reference (#8822) bd00773b25 fix(http): read response cookies when Headers has no getSetCookie (#8772) b1d200c40a Expose tag key on Schema tagged unions, closes #8681 (#8748) 13eb0099aa Fix PostgreSQL binary interval decoding (#8746) 4153144de5 Fix AsyncResult.flatMap data-last type inference (#8742) git-subtree-dir: repos/effect git-subtree-split: 269a7c864351231d42e6e95b7fa8f32050df3691 --- {repos/effect/.agents => .agents}/AGENTS.md | 0 .../skills/ai-docs/SKILL.md | 0 .../skills/bundle-analysis/SKILL.md | 0 .../skills/changesets/SKILL.md | 0 .../skills/changesets/authoring.md | 4 +- .../skills/ci-maintenance/SKILL.md | 0 .../ci-maintenance/privileged-workflows.md | 0 .../skills/dependency-maintenance/SKILL.md | 0 .../coordinated-upgrades.md | 0 .../dependency-maintenance/manifest-roles.md | 0 .../skills/effect-development/SKILL.md | 0 .../skills/jsdocs/SKILL.md | 0 .../skills/jsdocs/categories.md | 0 .../skills/jsdocs/declarations.md | 15 +- .../skills/jsdocs/examples.md | 0 .../skills/migration-guidance/SKILL.md | 0 .../skills/migration-guidance/annotations.md | 0 .../skills/migration-guidance/generation.md | 0 .../skills/package-development/SKILL.md | 0 .../package-development/dependencies.md | 0 .../skills/package-development/publishing.md | 0 .../package-development/registration.md | 0 .../skills/performance-analysis/SKILL.md | 0 .../skills/performance-analysis/runtime.md | 0 .../skills/performance-analysis/types.md | 0 .../skills/test-development/SKILL.md | 0 .../test-development/displayed-types.md | 0 .../skills/test-development/runtime.md | 0 .../skills/test-development/types.md | 0 .../skills/vendored-assets/SKILL.md | 0 .../skills/vendored-assets/browser-assets.md | 0 .changeset/README.md | 8 - .changeset/active-query-invalidation.md | 5 - .changeset/add-oxlint-integration.md | 5 - .changeset/add-stream-mutation.md | 13 - .changeset/adopt-module-system.md | 8 - .changeset/align-invalidation-entry-types.md | 12 - .changeset/align-schema-void-ts-semantics.md | 12 - .changeset/all-phones-dream.md | 5 - .changeset/amber-garlics-sing.md | 5 - .changeset/api-client-isolate-layers.md | 20 - .changeset/atom-query-vue.md | 6 - .changeset/batch-cosmos-acks.md | 5 - .changeset/batch-dual-options.md | 5 - .changeset/big-teams-wash.md | 5 - .changeset/blue-rats-admire.md | 5 - .changeset/bold-berries-change.md | 5 - .changeset/brave-animals-like.md | 5 - .changeset/brave-bushes-fold.md | 5 - .changeset/brave-laws-add.md | 5 - .changeset/brave-oranges-try.md | 5 - .changeset/brave-years-fry.md | 5 - .changeset/bright-camels-grin.md | 5 - .changeset/bright-lions-share.md | 5 - .changeset/bright-mugs-paint.md | 5 - .changeset/bright-peaches-wave.md | 5 - .changeset/bright-trees-count.md | 5 - .changeset/builtin-map-union-receiver.md | 5 - .changeset/cached-per-request-helper.md | 5 - .changeset/calm-clocks-observe.md | 6 - .changeset/calm-cosmos-cluster.md | 5 - .changeset/calm-filter-cycle.md | 5 - .changeset/calm-pallets-listen.md | 5 - .changeset/calm-pets-float.md | 10 - .changeset/calm-picks-refresh.md | 6 - .changeset/chilly-colts-attack.md | 5 - .changeset/chilly-steaks-rule.md | 5 - .changeset/chubby-dolls-yell.md | 6 - .changeset/class-facade-encode-keys.md | 6 - .changeset/clean-atom-suspense.md | 5 - .changeset/clean-sql-data-column.md | 5 - .changeset/clean-unused-dependencies.md | 8 - .changeset/cleanup-tsgolint-codegen.md | 15 - .changeset/clear-cycles-enjoy.md | 9 - .changeset/clever-breads-enter.md | 6 - .changeset/clever-pets-follow.md | 6 - .changeset/cli-preserve-trailing-newline.md | 5 - .changeset/cli-sync-no-tags.md | 5 - .changeset/cli-ue-semver-target.md | 5 - .changeset/client-fetch-property.md | 7 - .../cluster-servicebus-notifications.md | 5 - .changeset/codegen-rule-options.md | 5 - .changeset/cold-brooms-ski.md | 5 - .changeset/cold-rabbits-pay.md | 5 - .changeset/command-button-optional-input.md | 5 - .changeset/commander-state-toast-types.md | 8 - .changeset/commander-stream-fn.md | 23 - .changeset/common-schools-stop.md | 5 - .changeset/computed-projections-extensions.md | 12 - .changeset/computed-projections-infra.md | 5 - .changeset/concurrent-decode-helpers.md | 14 - .changeset/config-nested-native.md | 5 - .changeset/config.json | 58 +- .../configurable-barrel-import-extension.md | 5 - .../configurable-mutation-invalidation.md | 21 - .changeset/context-map-resolver-cache.md | 5 - .changeset/cool-needles-wash.md | 5 - .changeset/core-boundary-split.md | 9 - .changeset/cosmos-autoscale-containers.md | 6 - .changeset/cosmos-cluster-occ-tests.md | 5 - .changeset/cosmos-every-param-shift.md | 5 - .changeset/cosmos-filter-seed.md | 5 - .changeset/cosmos-namespace-spans.md | 5 - .changeset/cosmos-namespace-support.md | 5 - .changeset/cosmos-span-response-size.md | 7 - .changeset/cosmos-workflow-engine.md | 5 - .changeset/cozy-cats-lie.md | 5 - .changeset/crisp-seals-care.md | 11 - .changeset/crisp-trees-smash.md | 5 - .changeset/ctxmap-per-request-memomap.md | 24 - .changeset/cuddly-laws-smell.md | 5 - .changeset/cups-error-tagged.md | 9 - .changeset/curly-beds-sing.md | 5 - .changeset/curly-dolls-double.md | 5 - .changeset/curvy-files-share.md | 5 - .changeset/curvy-pugs-rush.md | 5 - .changeset/cyan-experts-smoke.md | 5 - .changeset/cyan-pianos-peel.md | 6 - .changeset/cyan-things-train.md | 5 - .changeset/date-defaults-pipe-self.md | 7 - .changeset/date-identifier-split.md | 8 - .changeset/delay-waiting-toast.md | 5 - .changeset/disable-query-invalidation.md | 19 - .changeset/document-query-project-modes.md | 5 - .changeset/drop-constructor-default.md | 7 - .changeset/easy-aliens-peel.md | 11 - .changeset/easy-spiders-study.md | 5 - .changeset/effa-sync-shared.md | 20 - .changeset/effa-sync-update-flag.md | 5 - .../effect-app-arbitrary-email-phone.md | 5 - .changeset/effect-app-prefer-fn-untraced.md | 5 - .../effect-app-realistic-number-samples.md | 5 - .changeset/effect-main-rc-apis.md | 17 - .changeset/eighty-aliens-hide.md | 5 - .changeset/eighty-signs-smile.md | 5 - .changeset/eleven-eyes-lie.md | 5 - .changeset/empty-queens-begin.md | 5 - .changeset/empty-rules-check.md | 5 - .changeset/eslint-codegen-glob-11.md | 5 - .changeset/expose-make-on-query-command.md | 5 - .../extract-fixed-nuxt-error-boundary.md | 5 - .changeset/facade-hardening-error-facade.md | 22 - .changeset/fair-baboons-marry.md | 5 - .changeset/fair-oranges-wave.md | 5 - .changeset/fair-tables-march.md | 5 - .changeset/famous-plums-watch.md | 5 - .changeset/fancy-ducks-run.md | 5 - .changeset/fast-loops-scream.md | 5 - .changeset/fiery-feet-sleep.md | 5 - .changeset/fiery-suits-kneel.md | 5 - .changeset/five-trams-repeat.md | 5 - .changeset/fix-class-type-export.md | 5 - .changeset/fix-cli-gist-cache-id.md | 5 - .changeset/fix-client-input-stream.md | 5 - .changeset/fix-contextmap-leak.md | 9 - .changeset/fix-cosmos-cluster-deferred.md | 5 - .changeset/fix-cosmos-cluster-machineid.md | 7 - .changeset/fix-cosmos-serverless-create.md | 5 - .changeset/fix-cosmos-workflow-engine.md | 10 - .changeset/fix-is-generator-function.md | 6 - .changeset/fix-json-schema-annotations.md | 5 - .changeset/fix-jsonschema-postprocess.md | 5 - .changeset/fix-onedsl-r-inference.md | 5 - .changeset/fix-pascalcase-barrel.md | 5 - .../fix-path-recursive-index-signature.md | 9 - .../fix-project-encodekeys-selection.md | 5 - .changeset/fix-projection-union-adapter.md | 5 - .../fix-query-defaultvalues-double-encode.md | 7 - .changeset/fix-repo-transformed-id-union.md | 5 - .changeset/fix-repo-transformed-id.md | 5 - .changeset/fix-resolver-cache-precheck.md | 5 - .changeset/fix-schema-interface-widening.md | 11 - .../fix-seed-marker-and-circular-dep.md | 5 - .changeset/fix-sql-array-relation-queries.md | 5 - .changeset/fix-sql-in-null-handling.md | 5 - .changeset/fix-sql-includes-serialize.md | 5 - .changeset/fix-sql-json-from-string-codec.md | 5 - .changeset/fix-sql-namespace-params.md | 5 - .../fix-sql-pg-seed-migrations-table.md | 5 - .changeset/fix-sql-select-etag-column.md | 5 - .../fix-sql-select-types-and-defaults.md | 5 - .changeset/fix-sql-whereevery-flip.md | 5 - ...fix-sqlite-per-ns-seed-migrations-table.md | 5 - .changeset/fix-transformed-struct-defaults.md | 5 - .../fix-vue-form-numeric-type-detection.md | 5 - .changeset/fix-vue-interrupt-tests.md | 5 - .changeset/fix-vue-lint-typescript-version.md | 5 - .changeset/flat-turkeys-invent.md | 5 - .changeset/flatten-nested-anyof.md | 5 - .changeset/forty-bottles-tease.md | 6 - .changeset/frank-ends-switch.md | 5 - .changeset/frank-memes-sin.md | 5 - .changeset/frank-papayas-design.md | 5 - .changeset/fresh-buckets-pump.md | 5 - .changeset/fresh-candles-shake.md | 11 - .changeset/fresh-indexed-fields.md | 5 - .changeset/fresh-mails-visit.md | 6 - .changeset/fresh-paths-say.md | 6 - .changeset/fresh-pianos-argue.md | 5 - .changeset/friendly-points-drum.md | 5 - .changeset/fruity-meteors-fix.md | 11 - .changeset/full-oranges-heal.md | 6 - .changeset/full-pugs-fall.md | 5 - .changeset/funky-guests-switch.md | 5 - .changeset/funny-webs-clap.md | 5 - .changeset/fuzzy-eels-decide.md | 5 - .changeset/fuzzy-eggs-jump.md | 5 - .changeset/gentle-bats-leave.md | 5 - .changeset/gentle-donkeys-tease.md | 5 - .changeset/gold-ghosts-laugh.md | 5 - .changeset/gold-wolves-shake.md | 8 - .changeset/green-coins-talk.md | 5 - .changeset/green-frogs-check.md | 5 - .changeset/green-snakes-poke.md | 5 - .changeset/green-taxis-project-hash.md | 5 - .changeset/green-taxis-rule.md | 5 - .../handle-mutate-request-always-functions.md | 5 - .changeset/handler-input-make-aligned.md | 6 - .changeset/heavy-bears-appear.md | 5 - .changeset/heavy-ducks-show.md | 12 - .changeset/honest-planets-fry.md | 5 - .changeset/http-span-name.md | 5 - .changeset/huge-carrots-deny.md | 8 - .changeset/humble-walls-fail.md | 5 - .changeset/icy-paws-return.md | 5 - .changeset/indefinite-waiting-toast.md | 5 - .changeset/infra-generate-from-schema-seed.md | 5 - .changeset/infra-prefer-fn-untraced.md | 5 - .changeset/infra-sentry-node-dep.md | 5 - ...invalidation-set-add-shortcut-and-batch.md | 13 - .changeset/itchy-mangos-find.md | 5 - .changeset/jitm-json-store-boundary.md | 16 - .changeset/khaki-frogs-wave.md | 5 - .changeset/kind-lizards-bathe.md | 9 - .changeset/large-pugs-strive.md | 5 - .changeset/late-forks-obey.md | 6 - .changeset/lazy-boats-lay.md | 5 - .changeset/lazy-model-facades.md | 6 - .changeset/legal-weeks-raise.md | 7 - .changeset/lemon-emus-behave.md | 6 - .changeset/little-berries-sip.md | 9 - .changeset/little-planets-bake.md | 5 - .changeset/live-query-dependencies.md | 5 - .changeset/long-regions-change.md | 5 - .changeset/loud-planets-cheer.md | 5 - .changeset/lucky-papayas-fetch.md | 9 - .changeset/lucky-streets-add.md | 9 - .changeset/lucky-zebras-invent.md | 5 - .changeset/major-drinks-stop.md | 5 - .changeset/major-planets-lie.md | 5 - .changeset/many-ideas-report.md | 5 - .changeset/meta-codegen-no-export.md | 5 - .changeset/meta-module-name-override.md | 6 - .changeset/metal-mammals-smell.md | 6 - .../middleware-error-from-dynamic-rcm.md | 12 - .changeset/middleware-errors-via-rpc-tag.md | 29 - .changeset/mighty-hoops-help.md | 6 - .changeset/migrate-tsgolint.md | 10 - .changeset/modern-humans-chew.md | 5 - .changeset/moody-peas-smoke.md | 6 - .changeset/mutate-invalidation-shorthands.md | 30 - .changeset/mutate-wrap-stream-no-args.md | 5 - .changeset/namespaced-change-feed.md | 5 - .changeset/native-encoded-query-json.md | 9 - .changeset/neat-brooms-wave.md | 5 - .changeset/neat-dingos-tease.md | 5 - .changeset/neat-pumpkins-juggle.md | 5 - .changeset/new-badgers-wait.md | 9 - .changeset/new-berries-march.md | 7 - .changeset/nice-jars-grow.md | 5 - .changeset/nice-needles-film.md | 5 - .changeset/nice-shrimps-fold.md | 6 - .changeset/ninety-stamps-end.md | 11 - .changeset/no-await-effect.md | 5 - .changeset/number-input-dual-separator.md | 5 - .changeset/odd-deers-work.md | 7 - .changeset/odd-tigers-scope.md | 5 - .changeset/old-snakes-film.md | 5 - .changeset/old-suns-kneel.md | 5 - .changeset/olive-onions-lose.md | 9 - .changeset/omega-native-input-types.md | 5 - .changeset/omega-nullable-nested-struct.md | 5 - .changeset/omegaform-between-localization.md | 5 - .../omegaform-decodeto-meta-from-encoded.md | 5 - .changeset/omegaform-enter-submit-repro.md | 5 - .changeset/omegaform-input-registry.md | 5 - .changeset/omegaform-module-split.md | 9 - .changeset/omegaform-typecheck-stories.md | 5 - .changeset/omegaform-vnumber-input.md | 5 - .changeset/optimize-codegen-barrel-model.md | 15 - .changeset/otel-semconv-spans.md | 50 - .changeset/payload-summary-recurse.md | 17 - .changeset/per-namespace-seeding.md | 5 - .changeset/petite-tables-cover.md | 5 - .changeset/polite-chefs-drum.md | 6 - .changeset/pre.json | 496 - .changeset/project-computed-type-safety.md | 5 - .changeset/project-encoded-type-constraint.md | 5 - .changeset/project-union-schema.md | 5 - .changeset/project-union-subsets.md | 5 - .changeset/projectable-from-domain.md | 8 - .changeset/projectable-key-guard-project.md | 8 - .changeset/provide-dual.md | 5 - .changeset/publish-tsconfig-postpack.md | 9 - .changeset/pure-usesync-cleanup.md | 5 - .changeset/query-aggregate-support.md | 11 - .changeset/query-map-has-ops.md | 8 - .changeset/query-optional-mode.md | 5 - .changeset/query-span-propagation.md | 5 - .changeset/queryraw-stored-json.md | 8 - .changeset/quick-seals-relate.md | 9 - .changeset/quick-wolves-yawn.md | 5 - .changeset/quiet-shrimps-juggle.md | 7 - .changeset/quiet-tigers-reply.md | 5 - .changeset/quiet-tools-rest.md | 5 - .changeset/rare-llamas-begin.md | 5 - .changeset/rare-rules-help.md | 5 - .changeset/ready-bags-wave.md | 5 - .changeset/ready-bananas-scream.md | 11 - .changeset/real-lilies-design.md | 5 - .changeset/real-suits-clean.md | 5 - .changeset/recover-stuck-query-interrupts.md | 9 - .changeset/red-pillows-repeat.md | 5 - .changeset/relation-collect-fields.md | 11 - .changeset/remove-by-id-array.md | 5 - .changeset/remove-class-struct-pick-omit.md | 6 - .changeset/remove-legacy-from-make-client.md | 5 - .changeset/remove-legacy-mutate-result-2.md | 5 - .changeset/remove-lodash-from-effect-app.md | 5 - .changeset/remove-operations.md | 7 - .changeset/remove-queue-machinery.md | 6 - .changeset/remove-tagged-error-compat.md | 5 - .changeset/remove-tagged-request.md | 7 - .changeset/remove-void-schema-override.md | 5 - .changeset/remove-withDefaultConstructor.md | 7 - .changeset/rename-mutate-stream.md | 6 - ...e-withdefault-to-withconstructordefault.md | 9 - .changeset/replace-proxify-with-accessors.md | 6 - .changeset/repo-derived-query-invalidation.md | 7 - .../repo-mutations-accept-empty-arrays.md | 5 - .changeset/repo-span-topology.md | 5 - .changeset/repo-spans-itemtype.md | 5 - .changeset/repository-registry.md | 9 - .changeset/request-scoped-dependencies.md | 7 - .changeset/require-middleware-layers.md | 30 - .changeset/resolver-cache-use-official.md | 5 - .changeset/restore-tight-project-guard.md | 16 - .changeset/router-no-meta.md | 5 - .changeset/rpc-stream-support.md | 11 - .changeset/salty-weeks-walk.md | 5 - .changeset/schema-concurrency-unbounded.md | 5 - .changeset/schema-encode-slow-metrics.md | 9 - .../schema-facade-identifier-structfacade.md | 8 - .changeset/schema-json-collections.md | 5 - .changeset/secret-url-tests.md | 5 - .../select-delayed-cache-invalidation.md | 16 - .../sendgrid-configurable-fake-address.md | 5 - .../server-driven-cache-invalidation.md | 13 - .../server-invalidation-handler-shorthand.md | 17 - .changeset/servicebus-log-only-spans.md | 5 - .changeset/shaggy-terms-show.md | 5 - .changeset/shaggy-waves-slide.md | 5 - .changeset/shaggy-wolves-grow.md | 5 - .changeset/share-oxlintrc-config.md | 5 - .changeset/sharp-buses-bow.md | 5 - .changeset/sharp-seals-clean.md | 6 - .changeset/short-cats-march.md | 5 - .changeset/short-frogs-add.md | 5 - .changeset/short-suns-travel.md | 5 - .changeset/shy-squids-make.md | 5 - .changeset/silent-results-shave.md | 6 - .changeset/silent-rooms-throw.md | 5 - .changeset/silly-eggs-roll.md | 5 - .changeset/single-invalidate-query-call.md | 5 - .changeset/six-regions-start.md | 5 - .changeset/six-yaks-stop.md | 5 - .changeset/sixty-meals-sin.md | 5 - .changeset/slimy-apes-film.md | 6 - .changeset/slimy-lions-laugh.md | 5 - .changeset/slimy-schools-fold.md | 9 - .changeset/slow-readers-wave.md | 6 - .changeset/small-papers-sneeze.md | 5 - .changeset/small-wolves-knock.md | 5 - .changeset/smart-beers-listen.md | 6 - .changeset/smart-candles-rest.md | 6 - .changeset/soft-books-win.md | 5 - .changeset/soft-guests-relax.md | 5 - .changeset/soft-mails-brush.md | 5 - .changeset/soft-work-invalidations.md | 5 - .changeset/sour-turtles-smash.md | 5 - .changeset/source-first-package-exports.md | 7 - .changeset/sparkly-cars-press.md | 5 - .changeset/spicy-falcons-rush.md | 5 - .changeset/spicy-lights-trade.md | 5 - .changeset/sql-namespace-support.md | 5 - .changeset/sql-store-adapter.md | 5 - .changeset/sql-transaction-middleware.md | 5 - .changeset/sqlite-per-namespace-layermap.md | 5 - .changeset/sqlite-workflow-engine.md | 5 - .changeset/sse-context-map-scope.md | 5 - .changeset/sse-filter-namespace-by-storeid.md | 5 - .changeset/staged-publish-directory.md | 9 - .changeset/stale-actors-remain.md | 5 - .changeset/stale-emus-see.md | 5 - .changeset/stale-papers-join.md | 5 - .changeset/store-decode-schema-error.md | 6 - .changeset/store-effect-cached.md | 5 - .changeset/store-json-values.md | 8 - .changeset/store-seed-uninterruptible.md | 5 - .changeset/stream-callable-progress.md | 12 - .changeset/stream-drain-data-dependencies.md | 11 - .changeset/stream-effect-fail.md | 5 - .changeset/stream-final-default-success.md | 8 - .changeset/stream-final-type.md | 15 - .changeset/stream-first-write-flush.md | 5 - .changeset/stream-fn-enhancements.md | 12 - .changeset/stream-metadata-v1.md | 24 - .changeset/stream-on-command-query.md | 29 - .changeset/stream-withdefaulttoast.md | 8 - .changeset/strict-stream-handlers.md | 12 - .changeset/stringid-native-arbitrary.md | 6 - .changeset/strong-dots-sin.md | 5 - .changeset/struct-pick-omit.md | 5 - .changeset/structfacade-effect-app-struct.md | 7 - .changeset/suspense-keep-previous-value.md | 7 - .changeset/sweet-humans-ring.md | 5 - .changeset/sweet-moments-see.md | 5 - .changeset/swift-owls-knit.md | 5 - .changeset/swift-tables-turn.md | 11 - .changeset/sync-effect-app-command.md | 6 - .changeset/sync-effect-subtree.md | 5 - .changeset/sync-effect-url.md | 5 - .changeset/sync-push-deletions.md | 5 - .changeset/synchronous-change-feed.md | 5 - .../tagged-request-command-query-split.md | 8 - .changeset/tagged-request-for.md | 6 - .../tagged-union-drop-fields-constraint.md | 5 - .changeset/tagged-union-generate-guards.md | 10 - .changeset/tagged-union-generate-guards2.md | 10 - .changeset/tall-boxes-relax.md | 5 - .changeset/tall-hats-reflect.md | 6 - ...anstack-effect-equal-structural-sharing.md | 5 - .changeset/tanstack-suspense-seed.md | 5 - .changeset/tasty-eggs-stare.md | 5 - .changeset/tasty-fans-accept.md | 8 - .changeset/ten-crews-like.md | 5 - .changeset/ten-suns-shine.md | 6 - .changeset/tender-bars-ring.md | 5 - .changeset/tender-donkeys-pick.md | 8 - .changeset/thin-zebras-press.md | 5 - .changeset/thirty-cooks-pump.md | 5 - .changeset/thirty-experts-taste.md | 5 - .changeset/three-hotels-roll.md | 7 - .changeset/tidy-caches-reflect.md | 5 - .changeset/tidy-cosmos-batches.md | 5 - .changeset/tidy-derived-dependencies.md | 6 - .changeset/tidy-dprint-oxlint-config.md | 5 - .changeset/tidy-effects-diagnose.md | 5 - .changeset/tidy-forms-report.md | 5 - .changeset/tidy-pans-rule.md | 9 - .changeset/tidy-poets-think.md | 9 - .changeset/tidy-remaining-formatting.md | 6 - .changeset/tidy-rivers-identify.md | 5 - .changeset/tidy-titles-remove.md | 5 - .changeset/tiny-cows-repair.md | 5 - .changeset/tiny-gorillas-jump.md | 7 - .changeset/tired-insects-stand.md | 5 - .changeset/toast-group-request-id.md | 9 - .changeset/tough-apes-whisper.md | 6 - .../tsgo-decodingservices-workaround.md | 6 - .../tsgo-direct-schema-property-access.md | 9 - .changeset/twenty-seas-grab.md | 8 - .changeset/two-clocks-wash.md | 11 - .changeset/typed-database-error.md | 8 - .changeset/typed-query-builders.md | 12 - .changeset/unify-emailer-spans.md | 5 - .changeset/unify-parsemany-spans.md | 5 - .changeset/update-effect-4.0.0.md | 6 - .changeset/update-effect-4.0.1.md | 6 - .changeset/update-effect-beta-107.md | 24 - .changeset/update-effect-beta-36.md | 9 - .changeset/update-effect-beta-52.md | 9 - .../update-effect-beta-60-rpc-custom.md | 6 - .changeset/update-effect-beta-66.md | 7 - .changeset/update-effect-beta-67.md | 7 - .changeset/update-effect-beta-83.md | 14 - .changeset/update-effect-beta-84.md | 9 - .changeset/update-effect-beta-86.md | 9 - .changeset/update-effect-beta-88.md | 9 - .changeset/update-effect-beta-90.md | 9 - .changeset/update-effect-pkgprnew.md | 7 - .changeset/update-effect-rc-112.md | 17 - .changeset/update-effect-rc-115.md | 6 - .changeset/upset-aliens-bathe.md | 5 - .changeset/upset-bugs-roll.md | 5 - .changeset/version-independent-snapshot.md | 5 - .changeset/vue-components-src-exports.md | 5 - .../vue-components-vuetify-typecheck.md | 7 - .changeset/vue-placeholder-data-fallback.md | 5 - .changeset/vue-prefer-fn-untraced.md | 5 - .changeset/vue-stream-query.md | 5 - .changeset/vue-suspense-result-race.md | 5 - .../vue-unified-invalidation-foreach.md | 5 - .changeset/wacky-deer-decide.md | 5 - .changeset/warm-maps-reflect.md | 5 - .changeset/warm-streets-smell.md | 5 - .changeset/wet-roses-press.md | 5 - .changeset/wet-sites-fall.md | 8 - .changeset/wild-olives-bathe.md | 5 - .changeset/wild-wombats-nail.md | 5 - .changeset/wise-items-warn.md | 5 - .changeset/wise-stamps-crash.md | 5 - .changeset/withdefaulttoaststream-progress.md | 5 - .changeset/witty-bats-return.md | 9 - .changeset/wrap-stream-command.md | 43 - .claude/settings.json | 18 - .devcontainer/Dockerfile | 42 - .devcontainer/devcontainer.json | 14 - .devcontainer/humanlog.sh | 2 - .devcontainer/usepnpm.sh | 26 - .envrc | 4 - .githooks/README.md | 49 - .githooks/post-checkout | 98 - .githooks/pre-commit | 12 - .githooks/pre-push | 17 - .github/FUNDING.yml | 3 - .../actions/deploy-website/action.yml | 0 .../actions/setup/action.yaml | 0 .github/npm-trusted-publishing.md | 85 - .../workflows/ai-codegen.yml | 0 .../workflows/bundle-comment.yml | 0 .../.github => .github}/workflows/check.yml | 2 + .github/workflows/ci.yml | 179 - .github/workflows/claude.yml | 49 - .../.github => .github}/workflows/cluster.yml | 0 .github/workflows/effect-bot.yml | 39 + .github/workflows/publish-snapshot.yml | 105 - .github/workflows/publish-wiki.yml | 18 - .../workflows/release-queue.yml | 0 .github/workflows/release.yaml | 94 - .../.github => .github}/workflows/release.yml | 0 .../workflows/snapshot.yml | 0 .gitignore | 89 +- .ncurc.json | 20 - .npmrc | 1 - .nvmrc | 1 - .oxlintrc.json | 68 +- .specs/Update-to-latest-Effect.md | 23 - .specs/changeset-publishing.md | 138 - .vscode/extensions.json | 5 +- .vscode/launch.json | 22 - .vscode/model.code-snippets | 77 - .vscode/service.code-snippets | 14 - .vscode/settings.json | 84 +- .vscode/tasks.json | 38 - AGENTS.md | 217 - CLAUDE.md | 3 - repos/effect/LICENSE => LICENSE | 0 repos/effect/LLMS.md => LLMS.md | 0 repos/effect/MIGRATION.md => MIGRATION.md | 5 +- README.md | 210 +- {repos/effect/ai-docs => ai-docs}/README.md | 0 .../effect/ai-docs => ai-docs}/package.json | 4 +- .../src/01_effect/01_basics/01_effect-gen.ts | 0 .../src/01_effect/01_basics/02_effect-fn.ts | 0 .../01_basics/10_creating-effects.ts | 0 .../src/01_effect/01_basics/index.md | 0 .../01_effect/02_schema/10_schema-basics.ts | 0 .../src/01_effect/02_schema/index.md | 0 .../src/01_effect/03_services/01_service.ts | 0 .../src/01_effect/03_services/10_reference.ts | 0 .../03_services/20_layer-composition.ts | 0 .../01_effect/03_services/20_layer-unwrap.ts | 0 .../src/01_effect/03_services/index.md | 0 .../01_effect/04_errors/01_error-handling.ts | 0 .../src/01_effect/04_errors/10_catch-tags.ts | 0 .../01_effect/04_errors/20_reason-errors.ts | 0 .../src/01_effect/04_errors/index.md | 0 .../05_resources/10_acquire-release.ts | 0 .../05_resources/20_layer-side-effects.ts | 0 .../01_effect/05_resources/30_layer-map.ts | 0 .../src/01_effect/05_resources/index.md | 0 .../src/01_effect/06_running/10_run-main.ts | 0 .../01_effect/06_running/20_layer-launch.ts | 0 .../src/01_effect/06_running/index.md | 0 .../src/01_effect/07_pubsub/10_pubsub.ts | 0 .../src/01_effect/07_pubsub/index.md | 0 .../src/03_stream/10_creating-streams.ts | 0 .../src/03_stream/20_consuming-streams.ts | 0 .../src/03_stream/30_encoding.ts | 0 .../src/03_stream/index.md | 0 .../src/04_integration/10_managed-runtime.ts | 0 .../src/04_integration/index.md | 0 .../src/05_batching/10_request-resolver.ts | 0 .../src/05_batching/index.md | 0 .../src/06_schedule/10_schedules.ts | 0 .../src/06_schedule/index.md | 0 .../07_datetime/10_creating-and-formatting.ts | 0 .../src/07_datetime/20_time-zones.ts | 0 .../src/07_datetime/index.md | 0 .../src/08_observability/10_logging.ts | 0 .../src/08_observability/20_otlp-tracing.ts | 0 .../src/08_observability/index.md | 0 .../src/09_testing/10_effect-tests.ts | 0 .../src/09_testing/20_layer-tests.ts | 0 .../src/09_testing/index.md | 0 .../src/10_predicate/01_basics.ts | 0 .../src/10_predicate/index.md | 0 .../src/40_sql/10_basics.ts | 0 .../ai-docs => ai-docs}/src/40_sql/index.md | 0 .../src/50_http-client/10_basics.ts | 0 .../src/50_http-client/index.md | 0 .../src/51_http-server/10_basics.ts | 0 .../src/51_http-server/20_testing.ts | 0 .../src/51_http-server/fixtures/api/Api.ts | 0 .../fixtures/api/Authorization.ts | 0 .../src/51_http-server/fixtures/api/System.ts | 0 .../src/51_http-server/fixtures/api/Users.ts | 0 .../51_http-server/fixtures/domain/User.ts | 0 .../fixtures/domain/UserErrors.ts | 0 .../fixtures/server/Authorization.ts | 0 .../51_http-server/fixtures/server/Users.ts | 0 .../fixtures/server/Users/http.ts | 0 .../src/51_http-server/index.md | 0 .../10_working-with-child-processes.ts | 0 .../src/60_child-process/index.md | 0 .../src/70_cli/10_basics.ts | 0 .../ai-docs => ai-docs}/src/70_cli/index.md | 0 .../src/71_ai/10_language-model.ts | 0 .../ai-docs => ai-docs}/src/71_ai/20_tools.ts | 0 .../ai-docs => ai-docs}/src/71_ai/30_chat.ts | 0 .../src/71_ai/fixtures/domain/LaunchPlan.ts | 0 .../ai-docs => ai-docs}/src/71_ai/index.md | 0 .../src/80_cluster/10_entities.ts | 0 .../src/80_cluster/index.md | 0 .../effect/ai-docs => ai-docs}/src/index.md | 0 .../effect/ai-docs => ai-docs}/tsconfig.json | 3 +- assets/dev/effect-app-apple-touch-180.png | Bin 11603 -> 0 bytes repos/effect/deno.json => deno.json | 0 repos/effect/dprint.json => dprint.json | 0 dprint.jsonc | 6 - flake.lock | 46 +- flake.nix | 47 +- .../jsdocs.config.json => jsdocs.config.json | 1 - lint-staged.config.js | 29 - .../annotations/README.md | 0 .../annotations/effect__Arbitrary.yaml | 0 .../annotations/effect__Array.yaml | 0 .../annotations/effect__BigDecimal.yaml | 0 .../annotations/effect__BigInt.yaml | 0 .../annotations/effect__Brand.yaml | 0 .../annotations/effect__Cache.yaml | 0 .../annotations/effect__Cause.yaml | 0 .../annotations/effect__Channel.yaml | 0 .../effect__ChildExecutorDecision.yaml | 0 .../annotations/effect__Chunk.yaml | 0 .../annotations/effect__Clock.yaml | 0 .../annotations/effect__Config.yaml | 0 .../annotations/effect__ConfigError.yaml | 0 .../annotations/effect__ConfigProvider.yaml | 0 .../effect__ConfigProviderPathPatch.yaml | 0 .../annotations/effect__Console.yaml | 0 .../annotations/effect__Context.yaml | 0 .../annotations/effect__Cron.yaml | 0 .../annotations/effect__Data.yaml | 0 .../annotations/effect__DateTime.yaml | 0 .../annotations/effect__DefaultServices.yaml | 0 .../annotations/effect__Deferred.yaml | 0 .../annotations/effect__Differ.yaml | 0 .../annotations/effect__Duration.yaml | 0 .../annotations/effect__Effect.yaml | 0 .../annotations/effect__Effectable.yaml | 0 .../annotations/effect__Either.yaml | 0 .../annotations/effect__Encoding.yaml | 0 .../annotations/effect__Equal.yaml | 0 .../annotations/effect__Equivalence.yaml | 0 .../annotations/effect__ExecutionPlan.yaml | 0 .../effect__ExecutionStrategy.yaml | 0 .../annotations/effect__Exit.yaml | 0 .../annotations/effect__FastCheck.yaml | 0 .../annotations/effect__Fiber.yaml | 0 .../annotations/effect__FiberHandle.yaml | 0 .../annotations/effect__FiberId.yaml | 0 .../annotations/effect__FiberMap.yaml | 0 .../annotations/effect__FiberRef.yaml | 0 .../annotations/effect__FiberRefs.yaml | 0 .../annotations/effect__FiberRefsPatch.yaml | 0 .../annotations/effect__FiberSet.yaml | 0 .../annotations/effect__FiberStatus.yaml | 0 .../annotations/effect__Function.yaml | 0 .../annotations/effect__GlobalValue.yaml | 0 .../annotations/effect__Graph.yaml | 0 .../annotations/effect__GroupBy.yaml | 0 .../annotations/effect__Hash.yaml | 0 .../annotations/effect__HashMap.yaml | 0 .../annotations/effect__HashSet.yaml | 0 .../annotations/effect__Inspectable.yaml | 0 .../annotations/effect__Iterable.yaml | 0 .../annotations/effect__JSONSchema.yaml | 0 .../annotations/effect__KeyedPool.yaml | 0 .../annotations/effect__Layer.yaml | 0 .../annotations/effect__LayerMap.yaml | 0 .../annotations/effect__List.yaml | 0 .../annotations/effect__LogLevel.yaml | 0 .../annotations/effect__LogSpan.yaml | 0 .../annotations/effect__Logger.yaml | 0 .../annotations/effect__Mailbox.yaml | 0 .../annotations/effect__ManagedRuntime.yaml | 0 .../annotations/effect__Match.yaml | 0 .../annotations/effect__MergeDecision.yaml | 0 .../annotations/effect__MergeState.yaml | 0 .../annotations/effect__MergeStrategy.yaml | 0 .../annotations/effect__Metric.yaml | 0 .../annotations/effect__MetricBoundaries.yaml | 0 .../annotations/effect__MetricHook.yaml | 0 .../annotations/effect__MetricKey.yaml | 0 .../annotations/effect__MetricKeyType.yaml | 0 .../annotations/effect__MetricLabel.yaml | 0 .../annotations/effect__MetricPair.yaml | 0 .../annotations/effect__MetricPolling.yaml | 0 .../annotations/effect__MetricRegistry.yaml | 0 .../annotations/effect__MetricState.yaml | 0 .../annotations/effect__Micro.yaml | 0 .../annotations/effect__ModuleVersion.yaml | 0 .../annotations/effect__MutableHashMap.yaml | 0 .../annotations/effect__MutableHashSet.yaml | 0 .../annotations/effect__MutableList.yaml | 0 .../annotations/effect__MutableQueue.yaml | 0 .../annotations/effect__MutableRef.yaml | 0 .../annotations/effect__Number.yaml | 0 .../annotations/effect__Option.yaml | 0 .../annotations/effect__Order.yaml | 0 .../annotations/effect__Ordering.yaml | 0 .../annotations/effect__ParseResult.yaml | 0 .../effect__PartitionedSemaphore.yaml | 0 .../annotations/effect__Pipeable.yaml | 0 .../annotations/effect__Pool.yaml | 0 .../annotations/effect__Predicate.yaml | 0 .../annotations/effect__Pretty.yaml | 0 .../annotations/effect__PubSub.yaml | 0 .../annotations/effect__Queue.yaml | 0 .../annotations/effect__Random.yaml | 0 .../annotations/effect__RateLimiter.yaml | 0 .../annotations/effect__RcMap.yaml | 0 .../annotations/effect__RcRef.yaml | 0 .../annotations/effect__Readable.yaml | 0 .../annotations/effect__Record.yaml | 0 .../annotations/effect__RedBlackTree.yaml | 0 .../annotations/effect__Redacted.yaml | 0 .../annotations/effect__Ref.yaml | 0 .../annotations/effect__Reloadable.yaml | 0 .../annotations/effect__Request.yaml | 0 .../annotations/effect__RequestBlock.yaml | 0 .../annotations/effect__RequestResolver.yaml | 0 .../annotations/effect__Resource.yaml | 0 .../annotations/effect__Runtime.yaml | 0 .../annotations/effect__RuntimeFlags.yaml | 0 .../effect__RuntimeFlagsPatch.yaml | 0 .../annotations/effect__STM.yaml | 0 .../annotations/effect__Schedule.yaml | 0 .../annotations/effect__ScheduleDecision.yaml | 0 .../annotations/effect__ScheduleInterval.yaml | 0 .../effect__ScheduleIntervals.yaml | 0 .../annotations/effect__Scheduler.yaml | 0 .../annotations/effect__Schema.yaml | 0 .../annotations/effect__SchemaAST.yaml | 0 .../annotations/effect__Scope.yaml | 0 .../annotations/effect__ScopedCache.yaml | 0 .../annotations/effect__ScopedRef.yaml | 0 .../annotations/effect__Secret.yaml | 0 .../effect__SingleProducerAsyncInput.yaml | 0 .../annotations/effect__Sink.yaml | 0 .../annotations/effect__SortedMap.yaml | 0 .../annotations/effect__SortedSet.yaml | 0 .../annotations/effect__Stream.yaml | 0 .../annotations/effect__StreamEmit.yaml | 0 .../effect__StreamHaltStrategy.yaml | 0 .../annotations/effect__Streamable.yaml | 0 .../annotations/effect__Struct.yaml | 0 .../annotations/effect__Subscribable.yaml | 0 .../annotations/effect__SubscriptionRef.yaml | 0 .../annotations/effect__Supervisor.yaml | 0 .../annotations/effect__Symbol.yaml | 0 .../annotations/effect__SynchronizedRef.yaml | 0 .../annotations/effect__TArray.yaml | 0 .../annotations/effect__TDeferred.yaml | 0 .../annotations/effect__TMap.yaml | 0 .../annotations/effect__TPriorityQueue.yaml | 0 .../annotations/effect__TPubSub.yaml | 0 .../annotations/effect__TQueue.yaml | 0 .../annotations/effect__TRandom.yaml | 0 .../annotations/effect__TReentrantLock.yaml | 0 .../annotations/effect__TRef.yaml | 0 .../annotations/effect__TSemaphore.yaml | 0 .../annotations/effect__TSet.yaml | 0 .../annotations/effect__TSubscriptionRef.yaml | 0 .../annotations/effect__Take.yaml | 0 .../annotations/effect__TestAnnotation.yaml | 0 .../effect__TestAnnotationMap.yaml | 0 .../annotations/effect__TestAnnotations.yaml | 0 .../annotations/effect__TestClock.yaml | 0 .../annotations/effect__TestConfig.yaml | 0 .../annotations/effect__TestContext.yaml | 0 .../annotations/effect__TestLive.yaml | 0 .../annotations/effect__TestServices.yaml | 0 .../annotations/effect__TestSized.yaml | 0 .../annotations/effect__Tracer.yaml | 0 .../annotations/effect__Trie.yaml | 0 .../annotations/effect__Tuple.yaml | 0 .../annotations/effect__Types.yaml | 0 .../effect__UpstreamPullRequest.yaml | 0 .../effect__UpstreamPullStrategy.yaml | 0 .../annotations/effect__Utils.yaml | 0 .../effect__ai-amazon-bedrock.yaml | 0 ...i-amazon-bedrock__AmazonBedrockClient.yaml | 0 ...i-amazon-bedrock__AmazonBedrockConfig.yaml | 0 ...n-bedrock__AmazonBedrockLanguageModel.yaml | 0 ...i-amazon-bedrock__AmazonBedrockSchema.yaml | 0 ..._ai-amazon-bedrock__AmazonBedrockTool.yaml | 0 ...i-amazon-bedrock__EventStreamEncoding.yaml | 0 .../effect__ai-amazon-bedrock__index.yaml | 0 ...effect__ai-anthropic__AnthropicClient.yaml | 0 ...effect__ai-anthropic__AnthropicConfig.yaml | 0 ..._ai-anthropic__AnthropicLanguageModel.yaml | 0 ...ect__ai-anthropic__AnthropicTokenizer.yaml | 0 .../effect__ai-anthropic__AnthropicTool.yaml | 0 .../effect__ai-anthropic__Generated.yaml | 0 .../effect__ai-anthropic__index.yaml | 0 .../annotations/effect__ai-google.yaml | 0 .../effect__ai-google__Generated.yaml | 0 .../effect__ai-google__GoogleClient.yaml | 0 .../effect__ai-google__GoogleConfig.yaml | 0 ...ffect__ai-google__GoogleLanguageModel.yaml | 0 .../effect__ai-google__GoogleTool.yaml | 0 .../annotations/effect__ai-google__index.yaml | 0 .../effect__ai-openai__Generated.yaml | 0 .../effect__ai-openai__OpenAiClient.yaml | 0 .../effect__ai-openai__OpenAiConfig.yaml | 0 ...fect__ai-openai__OpenAiEmbeddingModel.yaml | 0 ...ffect__ai-openai__OpenAiLanguageModel.yaml | 0 .../effect__ai-openai__OpenAiTelemetry.yaml | 15 + .../effect__ai-openai__OpenAiTokenizer.yaml | 0 .../effect__ai-openai__OpenAiTool.yaml | 0 .../annotations/effect__ai-openai__index.yaml | 0 .../effect__ai-openrouter__Generated.yaml | 0 ...fect__ai-openrouter__OpenRouterClient.yaml | 0 ...fect__ai-openrouter__OpenRouterConfig.yaml | 0 ...i-openrouter__OpenRouterLanguageModel.yaml | 0 .../effect__ai-openrouter__index.yaml | 0 .../annotations/effect__ai.yaml | 0 .../annotations/effect__ai__AiError.yaml | 0 .../annotations/effect__ai__Chat.yaml | 0 .../effect__ai__EmbeddingModel.yaml | 0 .../annotations/effect__ai__IdGenerator.yaml | 0 .../effect__ai__LanguageModel.yaml | 0 .../annotations/effect__ai__McpSchema.yaml | 0 .../annotations/effect__ai__McpServer.yaml | 0 .../annotations/effect__ai__Model.yaml | 0 .../annotations/effect__ai__Prompt.yaml | 0 .../annotations/effect__ai__Response.yaml | 0 .../annotations/effect__ai__Telemetry.yaml | 15 + .../annotations/effect__ai__Tool.yaml | 0 .../annotations/effect__ai__Toolkit.yaml | 0 .../annotations/effect__ai__index.yaml | 0 .../annotations/effect__cli.yaml | 0 .../annotations/effect__cli__Args.yaml | 0 .../annotations/effect__cli__AutoCorrect.yaml | 0 .../effect__cli__BuiltInOptions.yaml | 0 .../annotations/effect__cli__CliApp.yaml | 0 .../annotations/effect__cli__CliConfig.yaml | 0 .../annotations/effect__cli__Command.yaml | 0 .../effect__cli__CommandDescriptor.yaml | 0 .../effect__cli__CommandDirective.yaml | 0 .../annotations/effect__cli__ConfigFile.yaml | 0 .../annotations/effect__cli__HelpDoc.yaml | 0 .../effect__cli__HelpDoc__Span.yaml | 0 .../annotations/effect__cli__Options.yaml | 0 .../annotations/effect__cli__Primitive.yaml | 0 .../annotations/effect__cli__Prompt.yaml | 0 .../annotations/effect__cli__Usage.yaml | 0 .../effect__cli__ValidationError.yaml | 0 .../annotations/effect__cli__index.yaml | 0 .../annotations/effect__cluster.yaml | 0 .../effect__cluster__ClusterCron.yaml | 0 .../effect__cluster__ClusterError.yaml | 0 .../effect__cluster__ClusterSchema.yaml | 0 ...ffect__cluster__ClusterWorkflowEngine.yaml | 0 .../effect__cluster__DeliverAt.yaml | 0 .../annotations/effect__cluster__Entity.yaml | 0 .../effect__cluster__EntityAddress.yaml | 0 .../effect__cluster__EntityId.yaml | 0 .../effect__cluster__EntityProxy.yaml | 0 .../effect__cluster__EntityProxyServer.yaml | 0 .../effect__cluster__EntityResource.yaml | 0 .../effect__cluster__Envelope.yaml | 0 .../effect__cluster__HttpRunner.yaml | 0 .../effect__cluster__K8sHttpClient.yaml | 0 .../effect__cluster__MachineId.yaml | 0 .../annotations/effect__cluster__Message.yaml | 0 .../effect__cluster__MessageStorage.yaml | 0 .../annotations/effect__cluster__Reply.yaml | 0 .../annotations/effect__cluster__Runner.yaml | 0 .../effect__cluster__RunnerAddress.yaml | 0 .../effect__cluster__RunnerHealth.yaml | 0 .../effect__cluster__RunnerServer.yaml | 0 .../effect__cluster__RunnerStorage.yaml | 0 .../annotations/effect__cluster__Runners.yaml | 0 .../annotations/effect__cluster__ShardId.yaml | 0 .../effect__cluster__Sharding.yaml | 0 .../effect__cluster__ShardingConfig.yaml | 0 ...t__cluster__ShardingRegistrationEvent.yaml | 0 .../effect__cluster__SingleRunner.yaml | 0 .../effect__cluster__Singleton.yaml | 0 .../effect__cluster__SingletonAddress.yaml | 0 .../effect__cluster__Snowflake.yaml | 0 .../effect__cluster__SocketRunner.yaml | 0 .../effect__cluster__SqlMessageStorage.yaml | 0 .../effect__cluster__SqlRunnerStorage.yaml | 0 .../effect__cluster__TestRunner.yaml | 0 .../annotations/effect__cluster__index.yaml | 0 .../annotations/effect__experimental.yaml | 0 .../effect__experimental__DevTools.yaml | 0 ...ffect__experimental__DevTools__Client.yaml | 0 ...ffect__experimental__DevTools__Domain.yaml | 0 ...ffect__experimental__DevTools__Server.yaml | 0 .../effect__experimental__Event.yaml | 0 .../effect__experimental__EventGroup.yaml | 0 .../effect__experimental__EventJournal.yaml | 0 .../effect__experimental__EventLog.yaml | 0 .../effect__experimental__EventLogRemote.yaml | 0 .../effect__experimental__EventLogServer.yaml | 0 ...erimental__EventLogServer__Cloudflare.yaml | 0 .../effect__experimental__Machine.yaml | 0 ...ect__experimental__Machine__Procedure.yaml | 0 ..._experimental__Machine__ProcedureList.yaml | 0 ...l__Machine__SerializableProcedureList.yaml | 0 .../effect__experimental__PersistedCache.yaml | 0 .../effect__experimental__PersistedQueue.yaml | 0 ...__experimental__PersistedQueue__Redis.yaml | 0 .../effect__experimental__Persistence.yaml | 0 ...fect__experimental__Persistence__Lmdb.yaml | 0 ...ect__experimental__Persistence__Redis.yaml | 0 .../effect__experimental__RateLimiter.yaml | 0 ...ect__experimental__RateLimiter__Redis.yaml | 0 .../effect__experimental__Reactivity.yaml | 0 ...effect__experimental__RequestResolver.yaml | 0 .../effect__experimental__Sse.yaml | 0 .../effect__experimental__VariantSchema.yaml | 0 .../effect__experimental__index.yaml | 0 .../annotations/effect__index.yaml | 0 .../effect__opentelemetry__Logger.yaml | 0 .../effect__opentelemetry__Metrics.yaml | 0 .../effect__opentelemetry__NodeSdk.yaml | 0 .../effect__opentelemetry__Otlp.yaml | 0 .../effect__opentelemetry__OtlpLogger.yaml | 0 .../effect__opentelemetry__OtlpMetrics.yaml | 0 .../effect__opentelemetry__OtlpResource.yaml | 0 .../effect__opentelemetry__OtlpTracer.yaml | 0 .../effect__opentelemetry__Resource.yaml | 0 .../effect__opentelemetry__Tracer.yaml | 0 .../effect__opentelemetry__WebSdk.yaml | 0 .../effect__opentelemetry__index.yaml | 0 ...__platform-browser__BrowserHttpClient.yaml | 0 ...fect__platform-browser__BrowserWorker.yaml | 0 ...platform-browser__BrowserWorkerRunner.yaml | 0 .../effect__platform-browser__Clipboard.yaml | 0 ...effect__platform-browser__Geolocation.yaml | 0 ...effect__platform-browser__Permissions.yaml | 0 .../effect__platform-browser__index.yaml | 0 ...ect__platform-bun__BunCommandExecutor.yaml | 0 .../effect__platform-bun__BunContext.yaml | 0 ...orm-bun__BunFileSystem__ParcelWatcher.yaml | 0 ...effect__platform-bun__BunHttpPlatform.yaml | 0 .../effect__platform-bun__BunHttpServer.yaml | 0 ...t__platform-bun__BunHttpServerRequest.yaml | 0 ...ffect__platform-bun__BunKeyValueStore.yaml | 0 .../effect__platform-bun__BunSink.yaml | 0 .../effect__platform-bun__BunSocket.yaml | 0 ...effect__platform-bun__BunSocketServer.yaml | 0 .../effect__platform-bun__BunStream.yaml | 0 .../effect__platform-bun__BunWorker.yaml | 0 .../effect__platform-bun__index.yaml | 0 ...form-node-shared__NodeCommandExecutor.yaml | 0 ...shared__NodeFileSystem__ParcelWatcher.yaml | 0 ...atform-node-shared__NodeKeyValueStore.yaml | 0 ...__platform-node-shared__NodeMultipart.yaml | 0 ...ffect__platform-node-shared__NodeSink.yaml | 0 ...ect__platform-node-shared__NodeSocket.yaml | 0 ...ect__platform-node-shared__NodeStream.yaml | 0 ...t__platform-node__NodeCommandExecutor.yaml | 0 .../effect__platform-node__NodeContext.yaml | 0 ...m-node__NodeFileSystem__ParcelWatcher.yaml | 0 ...effect__platform-node__NodeHttpClient.yaml | 0 ...effect__platform-node__NodeHttpServer.yaml | 0 ...ect__platform-node__NodeKeyValueStore.yaml | 0 .../effect__platform-node__NodeWorker.yaml | 0 .../effect__platform-node__Undici.yaml | 0 .../effect__platform-node__index.yaml | 0 .../annotations/effect__platform.yaml | 0 .../effect__platform__ChannelSchema.yaml | 0 .../effect__platform__Command.yaml | 0 .../effect__platform__CommandExecutor.yaml | 0 .../effect__platform__Cookies.yaml | 0 .../effect__platform__Effectify.yaml | 0 .../annotations/effect__platform__Error.yaml | 0 .../annotations/effect__platform__Etag.yaml | 0 .../effect__platform__FetchHttpClient.yaml | 0 .../effect__platform__FileSystem.yaml | 0 .../effect__platform__Headers.yaml | 0 .../effect__platform__HttpApi.yaml | 0 .../effect__platform__HttpApiBuilder.yaml | 0 .../effect__platform__HttpApiClient.yaml | 0 .../effect__platform__HttpApiEndpoint.yaml | 0 .../effect__platform__HttpApiError.yaml | 0 .../effect__platform__HttpApiGroup.yaml | 0 .../effect__platform__HttpApiMiddleware.yaml | 0 .../effect__platform__HttpApiScalar.yaml | 0 .../effect__platform__HttpApiSchema.yaml | 0 .../effect__platform__HttpApiSecurity.yaml | 0 .../effect__platform__HttpApiSwagger.yaml | 0 .../effect__platform__HttpApp.yaml | 0 .../effect__platform__HttpBody.yaml | 0 .../effect__platform__HttpClient.yaml | 0 .../effect__platform__HttpClientError.yaml | 0 .../effect__platform__HttpClientRequest.yaml | 0 .../effect__platform__HttpClientResponse.yaml | 0 ...effect__platform__HttpIncomingMessage.yaml | 0 .../effect__platform__HttpLayerRouter.yaml | 0 .../effect__platform__HttpMethod.yaml | 0 .../effect__platform__HttpMiddleware.yaml | 0 .../effect__platform__HttpMultiplex.yaml | 0 .../effect__platform__HttpPlatform.yaml | 0 .../effect__platform__HttpRouter.yaml | 0 .../effect__platform__HttpServer.yaml | 0 .../effect__platform__HttpServerError.yaml | 0 .../effect__platform__HttpServerRequest.yaml | 0 ...fect__platform__HttpServerRespondable.yaml | 0 .../effect__platform__HttpServerResponse.yaml | 0 .../effect__platform__KeyValueStore.yaml | 0 .../effect__platform__MsgPack.yaml | 0 .../effect__platform__Multipart.yaml | 0 .../annotations/effect__platform__Ndjson.yaml | 0 .../effect__platform__OpenApi.yaml | 0 .../effect__platform__OpenApiJsonSchema.yaml | 0 .../annotations/effect__platform__Path.yaml | 0 ...ect__platform__PlatformConfigProvider.yaml | 0 .../effect__platform__PlatformLogger.yaml | 0 .../effect__platform__Runtime.yaml | 0 .../annotations/effect__platform__Socket.yaml | 0 .../effect__platform__SocketServer.yaml | 0 .../effect__platform__Template.yaml | 0 .../effect__platform__Terminal.yaml | 0 .../effect__platform__Transferable.yaml | 0 .../annotations/effect__platform__Url.yaml | 0 .../effect__platform__UrlParams.yaml | 0 .../annotations/effect__platform__Worker.yaml | 0 .../effect__platform__WorkerError.yaml | 0 .../effect__platform__WorkerRunner.yaml | 0 .../annotations/effect__platform__index.yaml | 0 .../annotations/effect__printer-ansi.yaml | 0 .../effect__printer-ansi__Ansi.yaml | 0 .../effect__printer-ansi__AnsiDoc.yaml | 0 .../effect__printer-ansi__Color.yaml | 0 .../effect__printer-ansi__index.yaml | 0 .../annotations/effect__printer.yaml | 0 .../annotations/effect__printer__Doc.yaml | 0 .../effect__printer__DocStream.yaml | 0 .../annotations/effect__printer__DocTree.yaml | 0 .../annotations/effect__printer__Flatten.yaml | 0 .../annotations/effect__printer__Layout.yaml | 0 .../effect__printer__Optimize.yaml | 0 .../effect__printer__PageWidth.yaml | 0 .../annotations/effect__printer__index.yaml | 0 .../annotations/effect__rpc.yaml | 0 .../annotations/effect__rpc__Rpc.yaml | 0 .../annotations/effect__rpc__RpcClient.yaml | 0 .../effect__rpc__RpcClientError.yaml | 0 .../annotations/effect__rpc__RpcGroup.yaml | 0 .../annotations/effect__rpc__RpcMessage.yaml | 0 .../effect__rpc__RpcMiddleware.yaml | 0 .../annotations/effect__rpc__RpcSchema.yaml | 0 .../effect__rpc__RpcSerialization.yaml | 0 .../annotations/effect__rpc__RpcServer.yaml | 0 .../annotations/effect__rpc__RpcTest.yaml | 0 .../annotations/effect__rpc__index.yaml | 0 ...ect__sql-clickhouse__ClickhouseClient.yaml | 0 ...t__sql-clickhouse__ClickhouseMigrator.yaml | 0 .../effect__sql-clickhouse__index.yaml | 0 .../annotations/effect__sql-d1__D1Client.yaml | 0 .../annotations/effect__sql-d1__index.yaml | 0 .../effect__sql-drizzle__Mysql.yaml | 0 .../annotations/effect__sql-drizzle__Pg.yaml | 0 .../effect__sql-drizzle__Sqlite.yaml | 0 .../effect__sql-kysely__Kysely.yaml | 0 .../effect__sql-kysely__Mssql.yaml | 0 .../effect__sql-kysely__Mysql.yaml | 0 .../annotations/effect__sql-kysely__Pg.yaml | 0 .../effect__sql-kysely__Sqlite.yaml | 0 .../effect__sql-kysely__patch.types.yaml | 0 .../effect__sql-libsql__index.yaml | 0 .../effect__sql-mssql__MssqlClient.yaml | 0 .../effect__sql-mssql__Parameter.yaml | 0 .../effect__sql-mssql__Procedure.yaml | 0 .../annotations/effect__sql-mssql__index.yaml | 0 .../effect__sql-mysql2__MysqlClient.yaml | 0 .../effect__sql-mysql2__index.yaml | 0 .../annotations/effect__sql-pg__PgClient.yaml | 0 .../annotations/effect__sql-pg__index.yaml | 0 .../effect__sql-sqlite-bun__SqliteClient.yaml | 15 + .../effect__sql-sqlite-bun__index.yaml | 0 .../effect__sql-sqlite-do__SqliteClient.yaml | 0 .../effect__sql-sqlite-do__index.yaml | 0 ...effect__sql-sqlite-node__SqliteClient.yaml | 18 + .../effect__sql-sqlite-node__index.yaml | 0 ...sql-sqlite-react-native__SqliteClient.yaml | 0 ...ffect__sql-sqlite-react-native__index.yaml | 0 ...effect__sql-sqlite-wasm__SqliteClient.yaml | 0 .../effect__sql-sqlite-wasm__index.yaml | 0 .../annotations/effect__sql.yaml | 0 .../annotations/effect__sql__Migrator.yaml | 0 .../effect__sql__Migrator__FileSystem.yaml | 0 .../annotations/effect__sql__Model.yaml | 0 .../annotations/effect__sql__SqlClient.yaml | 0 .../effect__sql__SqlConnection.yaml | 0 .../annotations/effect__sql__SqlError.yaml | 0 .../effect__sql__SqlEventJournal.yaml | 0 .../effect__sql__SqlEventLogServer.yaml | 0 .../effect__sql__SqlPersistedQueue.yaml | 0 .../annotations/effect__sql__SqlResolver.yaml | 0 .../annotations/effect__sql__SqlSchema.yaml | 0 .../annotations/effect__sql__Statement.yaml | 0 .../annotations/effect__sql__index.yaml | 0 .../annotations/effect__typeclass.yaml | 0 .../effect__typeclass__Alternative.yaml | 0 .../effect__typeclass__Applicative.yaml | 0 .../effect__typeclass__Bicovariant.yaml | 0 .../effect__typeclass__Bounded.yaml | 0 .../effect__typeclass__Chainable.yaml | 0 .../effect__typeclass__Contravariant.yaml | 0 .../effect__typeclass__Coproduct.yaml | 0 .../effect__typeclass__Covariant.yaml | 0 .../effect__typeclass__Filterable.yaml | 0 .../effect__typeclass__FlatMap.yaml | 0 .../effect__typeclass__Foldable.yaml | 0 .../effect__typeclass__Invariant.yaml | 0 .../annotations/effect__typeclass__Monad.yaml | 0 .../effect__typeclass__Monoid.yaml | 0 .../annotations/effect__typeclass__Of.yaml | 0 .../effect__typeclass__Pointed.yaml | 0 .../effect__typeclass__Product.yaml | 0 .../effect__typeclass__SemiAlternative.yaml | 0 .../effect__typeclass__SemiApplicative.yaml | 0 .../effect__typeclass__SemiCoproduct.yaml | 0 .../effect__typeclass__SemiProduct.yaml | 0 .../effect__typeclass__Semigroup.yaml | 0 .../effect__typeclass__Traversable.yaml | 0 ...ect__typeclass__TraversableFilterable.yaml | 0 .../effect__typeclass__data__Array.yaml | 0 .../effect__typeclass__data__BigInt.yaml | 0 .../effect__typeclass__data__Boolean.yaml | 0 .../effect__typeclass__data__Duration.yaml | 0 .../effect__typeclass__data__Effect.yaml | 0 .../effect__typeclass__data__Either.yaml | 0 .../effect__typeclass__data__Identity.yaml | 0 .../effect__typeclass__data__Micro.yaml | 0 .../effect__typeclass__data__Number.yaml | 0 .../effect__typeclass__data__Option.yaml | 0 .../effect__typeclass__data__Ordering.yaml | 0 .../effect__typeclass__data__Predicate.yaml | 0 .../effect__typeclass__data__Record.yaml | 0 .../effect__typeclass__data__String.yaml | 0 .../effect__typeclass__data__Tuple.yaml | 0 .../annotations/effect__typeclass__index.yaml | 0 .../annotations/effect__vitest__index.yaml | 0 .../annotations/effect__vitest__utils.yaml | 0 .../annotations/effect__workflow.yaml | 0 .../effect__workflow__Activity.yaml | 0 .../effect__workflow__DurableClock.yaml | 0 .../effect__workflow__DurableDeferred.yaml | 0 .../effect__workflow__DurableQueue.yaml | 0 .../effect__workflow__DurableRateLimiter.yaml | 0 .../effect__workflow__Workflow.yaml | 0 .../effect__workflow__WorkflowEngine.yaml | 0 .../effect__workflow__WorkflowProxy.yaml | 0 ...effect__workflow__WorkflowProxyServer.yaml | 0 .../effect/migration => migration}/cause.md | 0 .../migration => migration}/equality.md | 0 .../migration => migration}/error-handling.md | 0 .../fiber-keep-alive.md | 0 .../migration => migration}/fiberref.md | 0 .../effect/migration => migration}/forking.md | 0 .../migration => migration}/generators.md | 0 .../layer-memoization.md | 0 .../effect/migration => migration}/runtime.md | 0 .../effect/migration => migration}/schema.md | 0 .../effect/migration => migration}/scope.md | 0 .../migration => migration}/services.md | 0 .../migration => migration}/v3-to-v4.md | 32 +- .../migration => migration}/yieldable.md | 0 old.patch | 0 package.json | 144 +- .../ai/anthropic/CHANGELOG.md | 17 + .../ai/anthropic/README.md | 0 .../ai/anthropic/codegen.yml | 0 .../ai/anthropic/package.json | 2 +- .../ai/anthropic/src/AnthropicClient.ts | 0 .../ai/anthropic/src/AnthropicConfig.ts | 4 + .../ai/anthropic/src/AnthropicError.ts | 3 + .../anthropic/src/AnthropicLanguageModel.ts | 16 +- .../ai/anthropic/src/AnthropicTelemetry.ts | 23 +- .../ai/anthropic/src/AnthropicTool.ts | 0 .../ai/anthropic/src/Generated.ts | 0 .../ai/anthropic/src/index.ts | 4 + .../ai/anthropic/src/internal/errors.ts | 0 .../ai/anthropic/src/internal/utilities.ts | 0 .../ai/anthropic/test/AnthropicClient.test.ts | 0 .../test/AnthropicLanguageModel.test.ts | 121 +- .../ai/anthropic/tsconfig.json | 0 .../typetest/AnthropicLanguageModel.tst.ts | 0 packages/ai/cloudflare/CHANGELOG.md | 17 + .../ai/cloudflare/README.md | 0 .../ai/cloudflare/package.json | 2 +- .../ai/cloudflare/src/CloudflareClient.ts | 0 .../cloudflare/src/CloudflareDecisionModel.ts | 0 .../ai/cloudflare/src/CloudflareSchema.ts | 0 .../ai/cloudflare/src/index.ts | 1 + .../ai/cloudflare/src/internal/errors.ts | 0 .../cloudflare/test/CloudflareClient.test.ts | 0 .../test/CloudflareDecisionModel.test.ts | 0 .../ai/cloudflare/tsconfig.json | 0 .../typetest/CloudflareDecisionModel.tst.ts | 0 .../ai/openai-compat/CHANGELOG.md | 17 + .../ai/openai-compat/README.md | 0 .../ai/openai-compat/package.json | 2 +- .../ai/openai-compat/src/OpenAiClient.ts | 0 .../ai/openai-compat/src/OpenAiConfig.ts | 4 + .../openai-compat/src/OpenAiEmbeddingModel.ts | 0 .../ai/openai-compat/src/OpenAiError.ts | 3 + .../openai-compat/src/OpenAiLanguageModel.ts | 3 +- .../ai/openai-compat/src/OpenAiTelemetry.ts | 58 +- .../ai/openai-compat/src/index.ts | 4 + .../ai/openai-compat/src/internal/errors.ts | 0 .../openai-compat/src/internal/utilities.ts | 0 .../openai-compat/test/OpenAiClient.test.ts | 0 .../test/OpenAiEmbeddingModel.test.ts | 0 .../test/OpenAiLanguageModel.test.ts | 0 .../test/OpenAiTelemetry.test.ts | 24 + .../ai/openai-compat/tsconfig.json | 0 .../typetest/OpenAiConfig.tst.ts | 0 .../ai/openai/CHANGELOG.md | 17 + .../packages => packages}/ai/openai/README.md | 0 .../ai/openai/codegen.yaml | 0 .../ai/openai/package.json | 2 +- .../ai/openai/src/Generated.ts | 0 .../ai/openai/src/OpenAiClient.ts | 0 .../ai/openai/src/OpenAiClientGenerated.ts | 0 .../ai/openai/src/OpenAiConfig.ts | 4 + .../ai/openai/src/OpenAiEmbeddingModel.ts | 0 .../ai/openai/src/OpenAiError.ts | 3 + .../ai/openai/src/OpenAiLanguageModel.ts | 3 +- .../ai/openai/src/OpenAiSchema.ts | 2 + .../ai/openai/src/OpenAiTelemetry.ts | 54 +- .../ai/openai/src/OpenAiTool.ts | 0 .../ai/openai/src/index.ts | 4 + .../ai/openai/src/internal/errors.ts | 0 .../ai/openai/src/internal/utilities.ts | 0 .../ai/openai/test/OpenAiClient.test.ts | 0 .../openai/test/OpenAiEmbeddingModel.test.ts | 0 .../openai/test/OpenAiLanguageModel.test.ts | 0 .../ai/openai/test/OpenAiSchema.test.ts | 0 .../ai/openai/test/OpenAiTelemetry.test.ts | 24 + .../ai/openai/tsconfig.json | 0 .../ai/openai/typetest/OpenAiTool.tst.ts | 0 .../ai/openrouter/CHANGELOG.md | 17 + .../ai/openrouter/README.md | 0 .../ai/openrouter/codegen.yml | 0 .../ai/openrouter/package.json | 2 +- .../ai/openrouter/src/Generated.ts | 0 .../ai/openrouter/src/OpenRouterClient.ts | 0 .../ai/openrouter/src/OpenRouterConfig.ts | 4 + .../openrouter/src/OpenRouterDecisionModel.ts | 0 .../ai/openrouter/src/OpenRouterError.ts | 3 + .../openrouter/src/OpenRouterLanguageModel.ts | 2 +- .../ai/openrouter/src/OpenRouterSchema.ts | 0 .../ai/openrouter/src/index.ts | 3 + .../ai/openrouter/src/internal/errors.ts | 0 .../ai/openrouter/src/internal/utilities.ts | 0 .../ai/openrouter/test/Generated.test.ts | 0 .../openrouter/test/OpenRouterClient.test.ts | 0 .../test/OpenRouterDecisionModel.test.ts | 0 .../test/OpenRouterLanguageModel.test.ts | 0 .../openrouter/test/OpenRouterSchema.test.ts | 0 .../ai/openrouter/tsconfig.json | 0 .../typetest/OpenRouterDecisionModel.tst.ts | 0 .../ai/typesafe/CHANGELOG.md | 8 + .../ai/typesafe/README.md | 0 .../ai/typesafe/package.json | 2 +- .../ai/typesafe/src/TypeSafeClient.ts | 0 .../ai/typesafe/src/TypeSafeConfig.ts | 4 + .../ai/typesafe/src/TypeSafeDecisionModel.ts | 0 .../ai/typesafe/src/TypeSafeSchema.ts | 0 .../ai/typesafe/src/index.ts | 2 + .../ai/typesafe/src/internal/errors.ts | 0 .../ai/typesafe/test/TypeSafeClient.test.ts | 0 .../test/TypeSafeDecisionModel.test.ts | 0 .../ai/typesafe/test/TypeSafeSchema.test.ts | 0 .../ai/typesafe/tsconfig.json | 0 .../typetest/TypeSafeDecisionModel.tst.ts | 0 .../atom/react/CHANGELOG.md | 10 + .../packages => packages}/atom/react/LICENSE | 0 .../atom/react/README.md | 0 .../atom/react/package.json | 2 +- .../atom/react/src/Hooks.ts | 63 +- .../atom/react/src/ReactHydration.ts | 3 + .../atom/react/src/RegistryContext.ts | 3 + .../atom/react/src/ScopedAtom.ts | 5 + .../atom/react/src/index.ts | 1 + .../atom/react/test/index.test.tsx | 124 + .../atom/react/tsconfig.json | 0 .../atom/react/vitest.setup.ts | 0 .../atom/solid/CHANGELOG.md | 8 + .../packages => packages}/atom/solid/LICENSE | 0 .../atom/solid/README.md | 0 .../atom/solid/package.json | 2 +- .../atom/solid/src/Hooks.ts | 12 + .../atom/solid/src/RegistryContext.ts | 3 + .../atom/solid/src/index.ts | 1 + .../atom/solid/test/index.test.tsx | 0 .../atom/solid/tsconfig.json | 0 .../atom/solid/vitest.setup.ts | 0 .../atom/vue/CHANGELOG.md | 8 + .../packages => packages}/atom/vue/LICENSE | 0 .../packages => packages}/atom/vue/README.md | 0 .../atom/vue/package.json | 2 +- .../atom/vue/src/index.ts | 62 +- .../atom/vue/test/index.test.ts | 0 .../atom/vue/tsconfig.json | 0 packages/cli/.gitignore | 0 packages/cli/.madgerc | 7 - packages/cli/.npmignore | 14 - packages/cli/CHANGELOG.md | 1657 -- packages/cli/bin.js | 2 - packages/cli/package.json | 108 - packages/cli/src/argv-patch.ts | 16 - packages/cli/src/extract.ts | 75 - packages/cli/src/gist.ts | 748 - packages/cli/src/index.ts | 903 - packages/cli/src/os-command.ts | 54 - packages/cli/src/shared.ts | 3 - packages/cli/src/sync-effect-app-subtree.ts | 307 - packages/cli/src/sync-effect-subtree.ts | 140 - packages/cli/src/sync-shared.ts | 349 - packages/cli/src/update-packages.ts | 4 - packages/cli/test.gists.yaml | 27 - packages/cli/test/argv-patch.test.ts | 198 - packages/cli/test/gist-cache.test.ts | 92 - packages/cli/test/update-packages.test.ts | 13 - packages/cli/tsconfig.json | 16 - packages/cli/vitest.config.ts | 5 - packages/e2e/CHANGELOG.md | 455 - packages/e2e/package.json | 34 - .../e2e/test/repoInvalidation.e2e.test.ts | 270 - packages/e2e/tsconfig.json | 28 - packages/e2e/vitest.config.ts | 13 - packages/effect-app/.madgerc | 7 - packages/effect-app/.npmignore | 14 - packages/effect-app/CHANGELOG.md | 9105 ------- packages/effect-app/package.json | 98 - packages/effect-app/src/Array.ts | 110 - packages/effect-app/src/Chunk.ts | 81 - packages/effect-app/src/Config.ts | 7 - packages/effect-app/src/Config/SecretURL.ts | 80 - .../src/Config/internal/configSecretURL.ts | 68 - packages/effect-app/src/ConfigProvider.ts | 5 - packages/effect-app/src/Context.ts | 181 - packages/effect-app/src/DataDependencies.ts | 171 - packages/effect-app/src/Effect.ts | 202 - packages/effect-app/src/Emailer.ts | 51 - packages/effect-app/src/Function.ts | 175 - packages/effect-app/src/Inputify.type.ts | 12 - packages/effect-app/src/Layer.ts | 69 - packages/effect-app/src/Model.ts | 5 - packages/effect-app/src/Model/Repository.ts | 7 - .../src/Model/Repository/Registry.ts | 35 - .../src/Model/Repository/dependency.ts | 32 - .../effect-app/src/Model/Repository/ext.ts | 374 - .../src/Model/Repository/internal/internal.ts | 988 - .../effect-app/src/Model/Repository/legacy.ts | 29 - .../src/Model/Repository/makeRepo.ts | 168 - .../src/Model/Repository/service.ts | 676 - .../src/Model/Repository/validation.ts | 38 - packages/effect-app/src/Model/dsl.ts | 129 - .../effect-app/src/Model/filter/filterApi.ts | 78 - packages/effect-app/src/Model/filter/types.ts | 6 - .../src/Model/filter/types/errors.ts | 47 - .../src/Model/filter/types/fields.ts | 50 - .../src/Model/filter/types/path/common.ts | 404 - .../src/Model/filter/types/path/eager.ts | 329 - .../src/Model/filter/types/path/index.ts | 4 - .../src/Model/filter/types/utils.ts | 128 - .../src/Model/filter/types/validator.ts | 46 - packages/effect-app/src/Model/query.ts | 13 - packages/effect-app/src/Model/query/dsl.ts | 3092 --- .../src/Model/query/new-kid-interpreter.ts | 532 - packages/effect-app/src/NonEmptySet.ts | 92 - packages/effect-app/src/Option.ts | 70 - packages/effect-app/src/Pure.ts | 450 - packages/effect-app/src/QueueMaker.ts | 11 - packages/effect-app/src/RequestContext.ts | 48 - .../src/RequestScopedDependencies.ts | 45 - packages/effect-app/src/Schema.ts | 499 - packages/effect-app/src/Schema/Class.ts | 683 - packages/effect-app/src/Schema/FastCheck.ts | 23 - packages/effect-app/src/Schema/Methods.ts | 8 - .../effect-app/src/Schema/SchemaParser.ts | 12 - .../src/Schema/SpecialJsonSchema.ts | 137 - .../effect-app/src/Schema/SpecialOpenApi.ts | 130 - packages/effect-app/src/Schema/brand.ts | 71 - packages/effect-app/src/Schema/email.ts | 32 - packages/effect-app/src/Schema/ext.ts | 688 - packages/effect-app/src/Schema/moreStrings.ts | 390 - packages/effect-app/src/Schema/numbers.ts | 162 - packages/effect-app/src/Schema/phoneNumber.ts | 30 - packages/effect-app/src/Schema/schema.ts | 5 - packages/effect-app/src/Schema/strings.ts | 69 - packages/effect-app/src/SchemaAST.ts | 1 - packages/effect-app/src/Set.ts | 665 - packages/effect-app/src/Store.ts | 325 - packages/effect-app/src/TypeTest.ts | 7 - packages/effect-app/src/Types.ts | 16 - packages/effect-app/src/Widen.type.ts | 26 - packages/effect-app/src/_ext/Array.ts | 36 - packages/effect-app/src/_ext/date.ts | 27 - packages/effect-app/src/_ext/misc.ts | 49 - packages/effect-app/src/_ext/ord.ext.ts | 40 - packages/effect-app/src/builtin-json.d.ts | 24 - packages/effect-app/src/builtin.ts | 53 - packages/effect-app/src/client.ts | 8 - .../effect-app/src/client/InvalidationKeys.ts | 55 - .../effect-app/src/client/apiClientFactory.ts | 464 - packages/effect-app/src/client/clientFor.ts | 186 - packages/effect-app/src/client/errors.ts | 310 - packages/effect-app/src/client/makeClient.ts | 629 - packages/effect-app/src/faker.ts | 32 - packages/effect-app/src/http.ts | 1 - packages/effect-app/src/http/Request.ts | 42 - packages/effect-app/src/http/internal/lib.ts | 13 - packages/effect-app/src/ids.ts | 66 - packages/effect-app/src/index.ts | 27 - packages/effect-app/src/logger.ts | 3 - packages/effect-app/src/middleware.ts | 28 - packages/effect-app/src/rpc.ts | 4 - packages/effect-app/src/rpc/Invalidation.ts | 278 - .../effect-app/src/rpc/MiddlewareMaker.ts | 492 - packages/effect-app/src/rpc/README.md | 41 - packages/effect-app/src/rpc/RpcContextMap.ts | 156 - packages/effect-app/src/rpc/RpcMiddleware.ts | 244 - packages/effect-app/src/runtime.ts | 57 - packages/effect-app/src/setupRequest.ts | 138 - packages/effect-app/src/toast.ts | 54 - packages/effect-app/src/transform.ts | 80 - packages/effect-app/src/utils.ts | 901 - packages/effect-app/src/utils/effectify.ts | 260 - packages/effect-app/src/utils/extend.ts | 28 - packages/effect-app/src/utils/gen.ts | 24 - packages/effect-app/src/utils/logLevel.ts | 17 - packages/effect-app/src/utils/logger.ts | 36 - packages/effect-app/src/validation.ts | 18 - .../effect-app/src/validation/validators.ts | 31 - packages/effect-app/src/withToast.ts | 133 - packages/effect-app/test/moreStrings.test.ts | 62 - packages/effect-app/test/rpc.test.ts | 62 - .../test/rpcMiddlewareContext.test.ts | 63 - packages/effect-app/test/schema.test.ts | 681 - packages/effect-app/test/secretURL.test.ts | 160 - packages/effect-app/test/special.test.ts | 1261 - packages/effect-app/test/utils.test.ts | 209 - packages/effect-app/tsconfig.base.json | 61 - packages/effect-app/tsconfig.json | 12 - packages/effect-app/tsconfig.src.json | 31 - packages/effect-app/tsconfig.test.json | 33 - packages/effect-app/vitest.config.ts | 5 - .../packages => packages}/effect/ARBITRARY.md | 0 .../packages => packages}/effect/CHANGELOG.md | 196 + .../packages => packages}/effect/CONFIG.md | 0 .../packages => packages}/effect/HTTPAPI.md | 0 .../packages => packages}/effect/LICENSE | 0 .../packages => packages}/effect/MCP.md | 0 .../packages => packages}/effect/OPTIC.md | 0 .../packages => packages}/effect/README.md | 0 .../packages => packages}/effect/SCHEMA.md | 0 .../effect/benchmark/Pool.ts | 0 .../effect/benchmark/http/multipart.ts | 0 .../benchmark/http/serverAllocations.ts | 0 .../effect/benchmark/http/tracer.ts | 0 .../benchmark/httpapi/sseClientDecoder.ts | 0 .../effect/benchmark/net/NetAddress.ts | 0 .../effect/benchmark/rpc/RpcSerialization.ts | 0 .../effect/benchmark/schema/Optic.ts | 0 .../effect/benchmark/schema/SchemaBinary.md | 0 .../effect/benchmark/schema/SchemaBinary.ts | 0 .../effect/benchmark/schema/SchemaError.ts | 0 .../effect/benchmark/stream/splitLines.ts | 0 .../packages => packages}/effect/package.json | 6 +- .../effect/runtimeperf/README.md | 0 .../effect/runtimeperf/compare.mts | 0 .../effect/runtimeperf/config.json | 0 .../effect/runtimeperf/materialize.mts | 0 .../effect/runtimeperf/run.mts | 0 .../effect/runtimeperf/stats.mts | 0 .../arbitrary/fixtures/fast-check-v4.ts | 0 .../suites/arbitrary/fixtures/native.ts | 0 .../suites/arbitrary/fixtures/schema.ts | 0 .../suites/compiler-rebuild/README.md | 0 .../suites/compiler-rebuild/costs.mts | 0 .../suites/compiler-rebuild/fixtures/aot.ts | 0 .../suites/compiler-rebuild/fixtures/cases.ts | 0 .../compiler-rebuild/fixtures/generate.mts | 0 .../compiler-rebuild/fixtures/interpreted.ts | 0 .../suites/compiler-rebuild/fixtures/jit.ts | 0 .../compiler-rebuild/fixtures/valibot.ts | 0 .../compiler-rebuild/fixtures/zod-cases.ts | 0 .../compiler-rebuild/fixtures/zod-compiled.ts | 0 .../compiler-rebuild/fixtures/zod-jitless.ts | 0 .../compiler-rebuild/report-resources.mts | 0 .../suites/compiler-rebuild/resources.mts | 0 .../suites/compiler-rebuild/run-resources.mts | 0 .../runtimeperf/suites/moltar/fixtures/aot.ts | 0 .../suites/moltar/fixtures/cases.ts | 0 .../suites/moltar/fixtures/data.ts | 0 .../suites/moltar/fixtures/generate.mts | 0 .../suites/moltar/fixtures/interpreted.ts | 0 .../runtimeperf/suites/moltar/fixtures/jit.ts | 0 .../suites/moltar/fixtures/valibot.ts | 0 .../runtimeperf/suites/moltar/fixtures/zod.ts | 0 .../suites/schema-benchmarks/fixtures/data.ts | 0 .../schema-benchmarks/fixtures/effect-beta.ts | 0 .../schema-benchmarks/fixtures/valibot.ts | 0 .../suites/schema-benchmarks/fixtures/zod.ts | 0 .../suites/schema-checked-sources/README.md | 0 .../schema-checked-sources/fixtures/cases.ts | 0 .../suites/schema-construction/AST-LAYOUT.md | 0 .../suites/schema-construction/README.md | 0 .../suites/schema-construction/RESULTS.md | 0 .../suites/schema-construction/TO-TYPE.md | 0 .../suites/schema-construction/fixtures.mts | 0 .../suites/schema-construction/profile.mts | 0 .../suites/schema-construction/run.mts | 0 .../suites/schema-construction/worker.mts | 0 .../fixtures/cases.ts | 0 .../suites/schema/fixtures/adapters.ts | 0 .../suites/schema/fixtures/behavior.ts | 0 .../suites/schema/fixtures/cold.ts | 0 .../suites/schema/fixtures/comparison.ts | 0 .../runtimeperf/test/materialize.test.mts | 0 .../effect/runtimeperf/test/registry.test.mts | 0 .../test/schema-construction.test.mts | 0 .../effect/runtimeperf/test/stats.test.mts | 0 .../effect/runtimeperf/test/worker.test.mts | 0 .../runtimeperf/test/worktree-diff.test.mts | 0 .../effect/runtimeperf/utils.mts | 0 .../effect/runtimeperf/worker.mts | 0 .../effect/runtimeperf/worktree-diff.mts | 0 .../effect/src/Arbitrary.ts | 0 .../packages => packages}/effect/src/Array.ts | 151 +- .../effect/src/BigDecimal.ts | 46 + .../effect/src/BigInt.ts | 36 + .../effect/src/Boolean.ts | 18 + .../packages => packages}/effect/src/Brand.ts | 10 + .../effect/src/ByteSize.ts | 57 + .../packages => packages}/effect/src/Cache.ts | 42 +- .../packages => packages}/effect/src/Cause.ts | 76 + .../effect/src/Channel.ts | 207 +- .../effect/src/ChannelSchema.ts | 7 + .../packages => packages}/effect/src/Chunk.ts | 82 + .../packages => packages}/effect/src/Clock.ts | 7 + .../effect/src/Combiner.ts | 10 + .../effect/src/Config.ts | 67 +- .../effect/src/ConfigProvider.ts | 22 + .../effect/src/Console.ts | 22 + .../effect/src/Context.ts | 30 + .../packages => packages}/effect/src/Cron.ts | 31 +- .../effect/src/Crypto.ts | 5 + .../packages => packages}/effect/src/Data.ts | 8 + .../effect/src/DateTime.ts | 97 + .../effect/src/Deferred.ts | 25 + .../effect/src/Differ.ts | 2 + .../effect/src/Duration.ts | 58 + .../effect/src/Effect.ts | 264 +- .../effect/src/Effectable.ts | 4 + .../packages => packages}/effect/src/Equal.ts | 13 +- .../effect/src/Equivalence.ts | 18 + .../effect/src/ErrorReporter.ts | 36 + .../effect/src/ExecutionPlan.ts | 72 +- .../packages => packages}/effect/src/Exit.ts | 32 + .../packages => packages}/effect/src/Fiber.ts | 14 + .../effect/src/FiberHandle.ts | 27 +- .../effect/src/FiberMap.ts | 20 + .../effect/src/FiberSet.ts | 15 + .../effect/src/FileSystem.ts | 16 + .../effect/src/Filter.ts | 31 + .../effect/src/Formatter.ts | 43 +- .../effect/src/Function.ts | 26 + .../packages => packages}/effect/src/Graph.ts | 219 +- .../packages => packages}/effect/src/HKT.ts | 5 + .../packages => packages}/effect/src/Hash.ts | 13 + .../effect/src/HashMap.ts | 41 + .../effect/src/HashRing.ts | 10 + .../effect/src/HashSet.ts | 21 + .../effect/src/Inspectable.ts | 8 + .../effect/src/Iterable.ts | 51 + .../effect/src/JsonPatch.ts | 5 + .../effect/src/JsonPointer.ts | 5 + .../effect/src/JsonSchema.ts | 17 + .../packages => packages}/effect/src/Latch.ts | 12 + .../packages => packages}/effect/src/Layer.ts | 58 +- .../effect/src/LayerMap.ts | 7 + .../effect/src/LayerRef.ts | 5 + .../effect/src/LogLevel.ts | 12 + .../effect/src/Logger.ts | 40 +- .../effect/src/ManagedRuntime.ts | 13 +- .../packages => packages}/effect/src/Match.ts | 48 + .../effect/src/Metric.ts | 45 + .../effect/src/MutableHashMap.ts | 18 + .../effect/src/MutableHashSet.ts | 11 + .../effect/src/MutableList.ts | 21 + .../effect/src/MutableRef.ts | 18 + .../effect/src/Newtype.ts | 9 + .../effect/src/NonEmptyIterable.ts | 4 + .../effect/src/Number.ts | 31 + .../packages => packages}/effect/src/Optic.ts | 25 + .../effect/src/Option.ts | 69 + .../packages => packages}/effect/src/Order.ts | 26 + .../effect/src/Ordering.ts | 5 + .../effect/src/PartitionedSemaphore.ts | 73 +- .../packages => packages}/effect/src/Path.ts | 6 + .../effect/src/Pipeable.ts | 7 + .../effect/src/PlatformError.ts | 8 + .../packages => packages}/effect/src/Pool.ts | 15 +- .../effect/src/Predicate.ts | 53 + .../effect/src/PrimaryKey.ts | 5 + .../effect/src/PubSub.ts | 85 +- .../packages => packages}/effect/src/Pull.ts | 16 + .../packages => packages}/effect/src/Queue.ts | 75 +- .../effect/src/Random.ts | 11 + .../packages => packages}/effect/src/RcMap.ts | 11 + .../packages => packages}/effect/src/RcRef.ts | 6 + .../effect/src/Record.ts | 46 + .../effect/src/Redactable.ts | 6 + .../effect/src/Redacted.ts | 8 + .../effect/src/Reducer.ts | 4 + .../packages => packages}/effect/src/Ref.ts | 18 + .../effect/src/References.ts | 21 + .../effect/src/RegExp.ts | 4 + .../effect/src/Request.ts | 22 + .../effect/src/RequestResolver.ts | 46 +- .../effect/src/Resource.ts | 7 + .../effect/src/Result.ts | 49 + .../effect/src/Runtime.ts | 10 + .../effect/src/Schedule.ts | 41 + .../effect/src/Scheduler.ts | 7 + .../effect/src/Schema.ts | 487 +- .../effect/src/SchemaAST.ts | 116 +- .../effect/src/SchemaGetter.ts | 78 +- .../effect/src/SchemaIssue.ts | 34 + .../effect/src/SchemaParser.ts | 26 + .../effect/src/SchemaRepresentation.ts | 148 + .../effect/src/SchemaTransformation.ts | 51 + .../packages => packages}/effect/src/Scope.ts | 15 + .../effect/src/ScopedCache.ts | 36 +- .../effect/src/ScopedRef.ts | 7 + .../effect/src/Semaphore.ts | 12 + .../packages => packages}/effect/src/Sink.ts | 85 +- .../effect/src/StandardSchema.ts | 1 + .../packages => packages}/effect/src/Stdio.ts | 7 + .../effect/src/Stream.ts | 299 +- .../effect/src/String.ts | 65 + .../effect/src/Struct.ts | 24 + .../effect/src/SubscriptionRef.ts | 27 + .../effect/src/Symbol.ts | 2 + .../effect/src/SynchronizedRef.ts | 25 + .../packages => packages}/effect/src/Take.ts | 3 + .../effect/src/Terminal.ts | 8 + .../effect/src/Tracer.ts | 23 + .../packages => packages}/effect/src/Trie.ts | 30 + .../packages => packages}/effect/src/Tuple.ts | 18 + .../effect/src/TxChunk.ts | 24 + .../effect/src/TxDeferred.ts | 9 + .../effect/src/TxHashMap.ts | 57 +- .../effect/src/TxHashSet.ts | 32 +- .../effect/src/TxPriorityQueue.ts | 26 +- .../effect/src/TxPubSub.ts | 20 + .../effect/src/TxQueue.ts | 39 + .../effect/src/TxReentrantLock.ts | 83 +- .../packages => packages}/effect/src/TxRef.ts | 8 + .../effect/src/TxSemaphore.ts | 68 +- .../effect/src/TxSubscriptionRef.ts | 13 + .../packages => packages}/effect/src/Types.ts | 36 + .../effect/src/UndefinedOr.ts | 9 + .../packages => packages}/effect/src/Unify.ts | 9 + .../packages => packages}/effect/src/Utils.ts | 4 + packages/effect/src/Version.ts | 38 + .../effect/src/ai/AiError.ts | 0 .../src/ai/AnthropicStructuredOutput.ts | 0 .../effect/src/ai/Chat.ts | 2 +- .../effect/src/ai/Decision.ts | 0 .../effect/src/ai/DecisionModel.ts | 43 +- .../effect/src/ai/EmbeddingModel.ts | 0 .../effect/src/ai/IdGenerator.ts | 0 .../effect/src/ai/LanguageModel.ts | 35 +- .../effect/src/ai/McpProtocol.ts | 0 .../effect/src/ai/McpSchema.ts | 0 .../effect/src/ai/McpServer.ts | 143 +- .../effect/src/ai/Model.ts | 0 .../effect/src/ai/OpenAiStructuredOutput.ts | 0 .../effect/src/ai/Prompt.ts | 0 .../effect/src/ai/Response.ts | 0 .../effect/src/ai/ResponseIdTracker.ts | 0 .../effect/src/ai/Telemetry.ts | 112 +- .../effect/src/ai/Tokenizer.ts | 0 .../effect/src/ai/Tool.ts | 0 .../effect/src/ai/Toolkit.ts | 0 .../effect/src/ai/index.ts | 0 .../src/ai/internal/codec-transformer.ts | 0 .../effect/src/ai/internal/mcpCore.ts | 0 .../effect/src/ai/internal/mcpProtocol.ts | 0 .../ai/internal/mcpProtocol/v2024_11_05.ts | 0 .../ai/internal/mcpProtocol/v2025_03_26.ts | 0 .../ai/internal/mcpProtocol/v2025_06_18.ts | 0 .../ai/internal/mcpProtocol/v2025_11_25.ts | 0 .../ai/internal/mcpProtocol/v2026_07_28.ts | 0 .../src/ai/internal/mcpProtocolRegistry.ts | 0 .../effect/src/ai/internal/mcpRuntime.ts | 127 +- .../src/ai/internal/mcpSchema/v2024_11_05.ts | 0 .../src/ai/internal/mcpSchema/v2025_03_26.ts | 0 .../src/ai/internal/mcpSchema/v2025_06_18.ts | 0 .../src/ai/internal/mcpSchema/v2025_11_25.ts | 0 .../src/ai/internal/mcpSchema/v2026_07_28.ts | 0 .../src/ai/internal/mcpStatefulRuntime.ts | 2 + .../src/ai/internal/structured-output.ts | 0 .../effect/src/cli/Argument.ts | 0 .../effect/src/cli/CliConfig.ts | 0 .../effect/src/cli/CliError.ts | 0 .../effect/src/cli/CliOutput.ts | 0 .../effect/src/cli/Command.ts | 0 .../effect/src/cli/Completions.ts | 0 .../effect/src/cli/Flag.ts | 0 .../effect/src/cli/GlobalFlag.ts | 0 .../effect/src/cli/HelpDoc.ts | 0 .../effect/src/cli/Param.ts | 0 .../effect/src/cli/Primitive.ts | 0 .../effect/src/cli/Prompt.ts | 0 .../effect/src/cli/index.ts | 0 .../effect/src/cli/internal/ansi.ts | 0 .../effect/src/cli/internal/auto-suggest.ts | 0 .../effect/src/cli/internal/command.ts | 0 .../src/cli/internal/completions/bash.ts | 0 .../cli/internal/completions/descriptor.ts | 0 .../src/cli/internal/completions/fish.ts | 0 .../src/cli/internal/completions/zsh.ts | 0 .../effect/src/cli/internal/config.ts | 0 .../effect/src/cli/internal/help.ts | 0 .../effect/src/cli/internal/lexer.ts | 0 .../effect/src/cli/internal/parser.ts | 0 .../effect/src/cli/internal/wizard.ts | 0 .../effect/src/cluster/ClusterCron.ts | 0 .../effect/src/cluster/ClusterError.ts | 0 .../effect/src/cluster/ClusterMetrics.ts | 0 .../effect/src/cluster/ClusterSchema.ts | 0 .../src/cluster/ClusterWorkflowEngine.ts | 0 .../effect/src/cluster/DeliverAt.ts | 0 .../effect/src/cluster/Entity.ts | 0 .../effect/src/cluster/EntityAddress.ts | 0 .../effect/src/cluster/EntityId.ts | 0 .../effect/src/cluster/EntityProxy.ts | 0 .../effect/src/cluster/EntityProxyServer.ts | 0 .../effect/src/cluster/EntityResource.ts | 0 .../effect/src/cluster/EntityType.ts | 0 .../effect/src/cluster/Envelope.ts | 0 .../effect/src/cluster/HttpRunner.ts | 0 .../effect/src/cluster/K8sHttpClient.ts | 14 +- .../effect/src/cluster/K8sTypes.ts | 0 .../effect/src/cluster/MachineId.ts | 0 .../effect/src/cluster/Message.ts | 0 .../effect/src/cluster/MessageStorage.ts | 0 .../effect/src/cluster/Reply.ts | 0 .../effect/src/cluster/Runner.ts | 0 .../effect/src/cluster/RunnerAddress.ts | 0 .../effect/src/cluster/RunnerHealth.ts | 0 .../effect/src/cluster/RunnerServer.ts | 0 .../effect/src/cluster/RunnerStorage.ts | 0 .../effect/src/cluster/Runners.ts | 0 .../effect/src/cluster/ShardId.ts | 0 .../effect/src/cluster/Sharding.ts | 0 .../effect/src/cluster/ShardingConfig.ts | 0 .../src/cluster/ShardingRegistrationEvent.ts | 0 .../effect/src/cluster/SingleRunner.ts | 0 .../effect/src/cluster/Singleton.ts | 0 .../effect/src/cluster/SingletonAddress.ts | 0 .../effect/src/cluster/Snowflake.ts | 0 .../effect/src/cluster/SocketRunner.ts | 0 .../effect/src/cluster/SqlMessageStorage.ts | 0 .../effect/src/cluster/SqlRunnerStorage.ts | 0 .../effect/src/cluster/TestRunner.ts | 0 .../effect/src/cluster/index.ts | 0 .../src/cluster/internal/clusterAbandon.ts | 0 .../src/cluster/internal/entityActivation.ts | 0 .../src/cluster/internal/entityManager.ts | 0 .../src/cluster/internal/entityReaper.ts | 0 .../effect/src/cluster/internal/hash.ts | 0 .../src/cluster/internal/interruptors.ts | 0 .../src/cluster/internal/resourceMap.ts | 0 .../src/cluster/internal/resourceRef.ts | 0 .../effect/src/cluster/internal/shardLock.ts | 0 .../effect/src/devtools/DevTools.ts | 0 .../effect/src/devtools/DevToolsClient.ts | 0 .../effect/src/devtools/DevToolsSchema.ts | 0 .../effect/src/devtools/DevToolsServer.ts | 0 .../effect/src/devtools/index.ts | 0 .../effect/src/encoding/Base64.ts | 4 + .../effect/src/encoding/Base64Url.ts | 4 + .../effect/src/encoding/EncodingError.ts | 5 + .../effect/src/encoding/Hex.ts | 5 + .../effect/src/encoding/Ini.ts | 0 .../effect/src/encoding/Ndjson.ts | 0 .../effect/src/encoding/SchemaBinary.ts | 0 .../effect/src/encoding/Sse.ts | 0 .../effect/src/encoding/Toml.ts | 4 +- .../effect/src/encoding/Yaml.ts | 12 +- .../effect/src/encoding/index.ts | 4 + .../effect/src/eventlog/Event.ts | 0 .../effect/src/eventlog/EventGroup.ts | 0 .../effect/src/eventlog/EventJournal.ts | 0 .../effect/src/eventlog/EventLog.ts | 0 .../effect/src/eventlog/EventLogEncryption.ts | 0 .../effect/src/eventlog/EventLogMessage.ts | 0 .../effect/src/eventlog/EventLogRemote.ts | 0 .../effect/src/eventlog/EventLogServer.ts | 0 .../src/eventlog/EventLogServerEncrypted.ts | 0 .../src/eventlog/EventLogServerUnencrypted.ts | 0 .../src/eventlog/EventLogSessionAuth.ts | 0 .../effect/src/eventlog/SqlEventJournal.ts | 22 +- .../eventlog/SqlEventLogServerEncrypted.ts | 0 .../eventlog/SqlEventLogServerUnencrypted.ts | 517 +- .../effect/src/eventlog/index.ts | 0 .../internal/identityRootSecretDerivation.ts | 0 .../effect/src/http-api/HttpApi.ts | 0 .../effect/src/http-api/HttpApiBuilder.ts | 6 +- .../effect/src/http-api/HttpApiClient.ts | 17 +- .../effect/src/http-api/HttpApiEndpoint.ts | 0 .../effect/src/http-api/HttpApiError.ts | 12 +- .../effect/src/http-api/HttpApiGroup.ts | 0 .../effect/src/http-api/HttpApiMiddleware.ts | 0 .../effect/src/http-api/HttpApiScalar.ts | 0 .../effect/src/http-api/HttpApiSchema.ts | 0 .../effect/src/http-api/HttpApiSecurity.ts | 16 +- .../effect/src/http-api/HttpApiSwagger.ts | 0 .../effect/src/http-api/HttpApiTest.ts | 0 .../effect/src/http-api/OpenApi.ts | 0 .../effect/src/http-api/index.ts | 0 .../effect/src/http-api/internal/html.ts | 0 .../src/http-api/internal/httpApiScalar.ts | 0 .../src/http-api/internal/httpApiSwagger.ts | 0 .../effect/src/http-api/internal/mediaType.ts | 0 .../effect/src/http-api/internal/path.ts | 0 .../effect/src/http/Cookies.ts | 0 .../effect/src/http/Etag.ts | 0 .../effect/src/http/FetchHttpClient.ts | 0 .../effect/src/http/FindMyWay.ts | 0 .../http/FindMyWay/internal/queryString.ts | 0 .../src/http/FindMyWay/internal/router.ts | 0 .../effect/src/http/Headers.ts | 10 +- .../effect/src/http/HttpBody.ts | 0 .../effect/src/http/HttpClient.ts | 71 +- .../effect/src/http/HttpClientError.ts | 0 .../effect/src/http/HttpClientRequest.ts | 0 .../effect/src/http/HttpClientResponse.ts | 4 +- .../effect/src/http/HttpEffect.ts | 13 +- .../effect/src/http/HttpIncomingMessage.ts | 0 .../effect/src/http/HttpMethod.ts | 0 .../effect/src/http/HttpMiddleware.ts | 105 +- .../effect/src/http/HttpPlatform.ts | 0 .../effect/src/http/HttpRouter.ts | 0 .../effect/src/http/HttpServer.ts | 2 +- .../effect/src/http/HttpServerError.ts | 0 .../effect/src/http/HttpServerRequest.ts | 0 .../effect/src/http/HttpServerRespondable.ts | 0 .../effect/src/http/HttpServerResponse.ts | 0 .../effect/src/http/HttpStaticServer.ts | 0 .../effect/src/http/HttpStatus.ts | 0 .../effect/src/http/HttpTraceContext.ts | 0 .../effect/src/http/Mime.ts | 0 .../effect/src/http/Multipart.ts | 0 .../effect/src/http/MultipartParser.ts | 0 .../src/http/MultipartParser/HeadersParser.ts | 0 .../effect/src/http/MultipartParser/Search.ts | 0 .../MultipartParser/internal/contentType.ts | 0 .../http/MultipartParser/internal/headers.ts | 0 .../MultipartParser/internal/multipart.ts | 0 .../http/MultipartParser/internal/search.ts | 0 .../effect/src/http/Template.ts | 0 .../effect/src/http/Url.ts | 0 .../effect/src/http/UrlParams.ts | 0 .../effect/src/http/index.ts | 0 .../effect/src/http/internal/compression.ts | 0 .../effect/src/http/internal/headers.ts | 0 .../effect/src/http/internal/httpBody.ts | 0 .../effect/src/http/internal/mimeTypes.ts | 0 .../src/http/internal/preResponseHandler.ts | 0 packages/effect/src/http/internal/tracing.ts | 52 + .../packages => packages}/effect/src/index.ts | 156 + .../effect/src/internal/arbitrary/array.ts | 6 +- .../src/internal/arbitrary/bigDecimal.ts | 0 .../effect/src/internal/arbitrary/bigInt.ts | 0 .../effect/src/internal/arbitrary/model.ts | 51 +- .../effect/src/internal/arbitrary/regexp.ts | 0 .../effect/src/internal/arbitrary/runner.ts | 14 +- .../effect/src/internal/arbitrary/schema.ts | 7 +- .../effect/src/internal/array.ts | 0 .../effect/src/internal/core.ts | 0 .../effect/src/internal/count.ts | 0 .../effect/src/internal/dateTime.ts | 0 .../effect/src/internal/doNotation.ts | 0 .../effect/src/internal/effect.ts | 92 +- .../effect/src/internal/equal.ts | 0 .../effect/src/internal/errors.ts | 0 .../effect/src/internal/executionPlan.ts | 0 .../effect/src/internal/graph.ts | 0 .../effect/src/internal/graphCsr.ts | 0 .../effect/src/internal/hash.ts | 0 .../effect/src/internal/hashMap.ts | 0 .../effect/src/internal/hashSet.ts | 0 .../effect/src/internal/layer.ts | 0 .../effect/src/internal/matcher.ts | 0 .../effect/src/internal/metric.ts | 0 .../effect/src/internal/option.ts | 0 .../effect/src/internal/persistence.ts | 0 .../effect/src/internal/random.ts | 0 .../effect/src/internal/rcRef.ts | 0 .../effect/src/internal/record.ts | 0 .../effect/src/internal/redacted.ts | 0 .../effect/src/internal/references.ts | 0 .../effect/src/internal/request.ts | 0 .../effect/src/internal/result.ts | 0 .../effect/src/internal/schedule.ts | 40 +- .../effect/src/internal/schema/annotations.ts | 0 .../effect/src/internal/schema/cause.ts | 0 .../effect/src/internal/schema/codegen.ts | 0 .../src/internal/schema/compilerRegistry.ts | 0 .../internal/schema/fromJsonSchemaDocument.ts | 0 .../src/internal/schema/fromRepresentation.ts | 0 .../effect/src/internal/schema/interpreter.ts | 0 .../effect/src/internal/schema/make.ts | 0 .../effect/src/internal/schema/parser.ts | 0 .../src/internal/schema/standardSchema.ts | 4 +- .../src/internal/schema/toCodeDocument.ts | 0 .../effect/src/internal/schema/toCodec.ts | 0 .../src/internal/schema/toDifferJsonPatch.ts | 0 .../src/internal/schema/toEncoderXml.ts | 0 .../src/internal/schema/toEquivalence.ts | 0 .../effect/src/internal/schema/toFormatter.ts | 0 .../effect/src/internal/schema/toIso.ts | 0 .../internal/schema/toJsonSchemaDocument.ts | 0 .../src/internal/schema/toRepresentation.ts | 0 .../effect/src/internal/schemaError.ts | 0 .../effect/src/internal/stackTraceLimit.ts | 0 .../effect/src/internal/stream.ts | 0 .../effect/src/internal/tracer.ts | 0 .../effect/src/internal/trie.ts | 0 .../effect/src/internal/ulid.ts | 0 .../effect/src/internal/uuid.ts | 0 packages/effect/src/internal/version.ts | 2 + .../effect/src/net/IpInterface.ts | 0 .../effect/src/net/IpNetwork.ts | 0 .../effect/src/net/NetAddress.ts | 0 .../effect/src/net/index.ts | 0 .../effect/src/observability/Otlp.ts | 0 .../effect/src/observability/OtlpExporter.ts | 15 +- .../effect/src/observability/OtlpLogger.ts | 41 +- .../effect/src/observability/OtlpMetrics.ts | 127 +- .../effect/src/observability/OtlpResource.ts | 45 +- .../src/observability/OtlpSerialization.ts | 0 .../effect/src/observability/OtlpTracer.ts | 74 +- .../src/observability/PrometheusMetrics.ts | 0 .../effect/src/observability/index.ts | 0 .../src/observability/internal/otlpEnv.ts | 0 .../observability/internal/otlpProtobuf.ts | 60 + .../src/observability/internal/protobuf.ts | 0 .../effect/src/persistence/KeyValueStore.ts | 0 .../effect/src/persistence/Persistable.ts | 0 .../effect/src/persistence/PersistedCache.ts | 0 .../effect/src/persistence/PersistedQueue.ts | 0 .../effect/src/persistence/Persistence.ts | 0 .../effect/src/persistence/RateLimiter.ts | 0 .../effect/src/persistence/Redis.ts | 0 .../effect/src/persistence/index.ts | 0 .../effect/src/process/ChildProcess.ts | 0 .../effect/src/process/ChildProcessSpawner.ts | 0 .../effect/src/process/index.ts | 0 .../effect/src/reactivity/AsyncResult.ts | 2 +- .../effect/src/reactivity/Atom.ts | 16 +- .../effect/src/reactivity/AtomHttpApi.ts | 6 +- .../effect/src/reactivity/AtomRef.ts | 0 .../effect/src/reactivity/AtomRegistry.ts | 7 + .../effect/src/reactivity/AtomRpc.ts | 0 .../effect/src/reactivity/Hydration.ts | 0 .../effect/src/reactivity/Reactivity.ts | 0 .../effect/src/reactivity/index.ts | 0 .../effect/src/rpc/Rpc.ts | 0 .../effect/src/rpc/RpcClient.ts | 72 +- .../effect/src/rpc/RpcClientError.ts | 0 .../effect/src/rpc/RpcGroup.ts | 0 .../effect/src/rpc/RpcMessage.ts | 0 .../effect/src/rpc/RpcMiddleware.ts | 0 .../effect/src/rpc/RpcSchema.ts | 0 .../effect/src/rpc/RpcSerialization.ts | 0 .../effect/src/rpc/RpcServer.ts | 50 +- .../effect/src/rpc/RpcTest.ts | 0 .../effect/src/rpc/RpcWorker.ts | 0 .../effect/src/rpc/Utils.ts | 0 .../effect/src/rpc/index.ts | 0 packages/effect/src/rpc/internal/tracing.ts | 22 + .../effect/src/schema/Model.ts | 0 .../effect/src/schema/SchemaAOTCompiler.ts | 0 .../src/schema/SchemaAOTCompiler/Build.ts | 0 .../effect/src/schema/SchemaCompiler.ts | 0 .../src/schema/SchemaCompiler/runtime.ts | 5 +- .../effect/src/schema/SchemaJITCompiler.ts | 0 .../src/schema/SchemaJITCompiler/enable.ts | 0 .../effect/src/schema/VariantSchema.ts | 0 .../effect/src/schema/index.ts | 0 .../effect/src/socket/Socket.ts | 0 .../effect/src/socket/SocketServer.ts | 0 .../effect/src/socket/index.ts | 0 .../effect/src/sql/Migrator.ts | 0 .../effect/src/sql/SqlClient.ts | 8 +- .../effect/src/sql/SqlConnection.ts | 0 .../effect/src/sql/SqlError.ts | 0 .../effect/src/sql/SqlModel.ts | 0 .../effect/src/sql/SqlResolver.ts | 0 .../effect/src/sql/SqlSchema.ts | 0 .../effect/src/sql/SqlStream.ts | 0 .../effect/src/sql/Statement.ts | 66 +- .../effect/src/sql/index.ts | 0 .../effect/src/testing/TestClock.ts | 9 + .../effect/src/testing/TestConsole.ts | 8 + .../effect/src/testing/TestSchema.ts | 0 .../effect/src/testing/index.ts | 3 + .../effect/src/workers/Transferable.ts | 0 .../effect/src/workers/Worker.ts | 0 .../effect/src/workers/WorkerError.ts | 0 .../effect/src/workers/WorkerRunner.ts | 0 .../effect/src/workers/index.ts | 0 .../effect/src/workflow/Activity.ts | 0 .../effect/src/workflow/DurableClock.ts | 0 .../effect/src/workflow/DurableDeferred.ts | 0 .../effect/src/workflow/DurableQueue.ts | 0 .../effect/src/workflow/Workflow.ts | 0 .../effect/src/workflow/WorkflowEngine.ts | 0 .../effect/src/workflow/WorkflowProxy.ts | 0 .../src/workflow/WorkflowProxyServer.ts | 0 .../effect/src/workflow/index.ts | 0 .../effect/src/workflow/internal/crypto.ts | 0 .../effect/test/Array.test.ts | 25 + .../effect/test/ArrayNaNIndex.test.ts | 0 .../effect/test/AtomRef.test.ts | 0 .../effect/test/BigDecimal.test.ts | 0 .../effect/test/BigInt.test.ts | 0 .../effect/test/Boolean.test.ts | 0 .../effect/test/Brand.test.ts | 0 .../effect/test/ByteSize.test.ts | 0 .../effect/test/Cache.test.ts | 32 + .../effect/test/Cause.test.ts | 0 .../effect/test/CauseMapAnnotations.test.ts | 0 .../effect/test/Channel.test.ts | 77 + .../effect/test/Chunk.test.ts | 0 .../effect/test/Clock.test.ts | 0 .../effect/test/Combiner.test.ts | 0 .../effect/test/Config.test.ts | 54 +- .../effect/test/ConfigProvider.test.ts | 0 .../effect/test/Context.test.ts | 0 .../effect/test/Cron.test.ts | 18 + .../effect/test/Crypto.test.ts | 0 .../effect/test/Data.test.ts | 0 .../effect/test/DateTime.test.ts | 0 .../effect/test/Deferred.test.ts | 0 .../effect/test/Duration.test.ts | 0 .../effect/test/Effect.test.ts | 98 + .../effect/test/EffectEager.test.ts | 0 .../effect/test/EffectKeepAlive.test.ts | 0 .../effect/test/Effectable.test.ts | 0 .../effect/test/Equal.test.ts | 0 .../effect/test/Equivalence.test.ts | 0 .../effect/test/ExecutionPlan.test.ts | 19 + .../effect/test/Exit.test.ts | 0 .../effect/test/Fiber.test.ts | 14 +- .../effect/test/FiberHandle.test.ts | 24 +- .../effect/test/FiberMap.test.ts | 0 .../effect/test/FiberSet.test.ts | 0 .../test/FiberSetRuntimePropagation.test.ts | 0 .../effect/test/FileSystem.test-utils.ts | 0 .../effect/test/Formatter.test.ts | 62 + .../effect/test/Function.test.ts | 0 .../effect/test/Graph.test.ts | 76 + .../effect/test/Hash.test.ts | 0 .../effect/test/HashMap.test.ts | 0 .../effect/test/HashRing.test.ts | 0 .../effect/test/HashSet.test.ts | 0 .../effect/test/HttpClient.test.ts | 0 .../effect/test/Iterable.test.ts | 0 .../effect/test/JsonPatch.test.ts | 0 .../effect/test/JsonPointer.test.ts | 0 .../effect/test/JsonSchema.test.ts | 0 .../effect/test/Latch.test.ts | 0 .../effect/test/Layer.test.ts | 23 + .../effect/test/LayerMap.test.ts | 0 .../effect/test/LayerRef.test.ts | 0 .../effect/test/LogLevel.test.ts | 0 .../effect/test/Logger.test.ts | 54 +- .../effect/test/ManagedRuntime.test.ts | 75 + .../effect/test/Match.test.ts | 0 .../effect/test/Metric.test.ts | 0 .../effect/test/Migrator.test.ts | 0 .../effect/test/MutableHashMap.test.ts | 0 .../effect/test/MutableHashSet.test.ts | 0 .../effect/test/MutableList.test.ts | 0 .../effect/test/Newtype.test.ts | 0 .../effect/test/Number.test.ts | 0 .../effect/test/Optic.test.ts | 0 .../effect/test/Option.test.ts | 0 .../effect/test/Order.test.ts | 0 .../effect/test/Ordering.test.ts | 0 .../effect/test/PartitionedSemaphore.test.ts | 45 + .../effect/test/Pathfinding.test.ts | 0 .../effect/test/Pool.test.ts | 21 + .../effect/test/Predicate.test.ts | 0 .../effect/test/PubSub.test.ts | 61 + .../effect/test/Pull.test.ts | 0 .../effect/test/Queue.test.ts | 68 +- .../effect/test/Random.test.ts | 0 .../effect/test/RcMap.test.ts | 0 .../effect/test/RcRef.test.ts | 0 .../effect/test/Record.test.ts | 0 .../effect/test/Redacted.test.ts | 0 .../effect/test/Reducer.test.ts | 0 .../effect/test/Ref.test.ts | 0 .../effect/test/Request.test.ts | 23 + .../effect/test/Resource.test.ts | 0 .../effect/test/Result.test.ts | 0 .../effect/test/Schedule.test.ts | 0 .../effect/test/Scheduler.test.ts | 0 .../effect/test/Scope.test.ts | 17 + .../effect/test/ScopedCache.test.ts | 27 + .../effect/test/ScopedRef.test.ts | 0 .../effect/test/Semaphore.test.ts | 0 .../effect/test/Sink.test.ts | 7 + .../effect/test/StackCapture.test.ts | 0 .../effect/test/StackTraceLimit.test.ts | 0 .../effect/test/Stdio.test.ts | 0 .../effect/test/Stream.test.ts | 99 + .../effect/test/String.test.ts | 0 .../effect/test/Struct.test.ts | 0 .../effect/test/SubscriptionRef.test.ts | 0 .../effect/test/Symbol.test.ts | 0 .../SynchronizedRef.modifySomeEffect.test.ts | 0 .../effect/test/SynchronizedRef.test.ts | 0 .../effect/test/TestClock.test.ts | 0 .../effect/test/Tracer.test.ts | 85 +- .../effect/test/Trie.test.ts | 0 .../effect/test/Tuple.test.ts | 0 .../effect/test/TxChunk.test.ts | 0 .../effect/test/TxDeferred.test.ts | 0 .../effect/test/TxHashMap.test.ts | 82 +- .../effect/test/TxHashSet.test.ts | 37 +- .../effect/test/TxPriorityQueue.test.ts | 25 +- .../effect/test/TxPubSub.test.ts | 0 .../effect/test/TxQueue.test.ts | 0 .../effect/test/TxReentrantLock.test.ts | 32 + .../effect/test/TxSemaphore.test.ts | 35 + .../effect/test/TxSubscriptionRef.test.ts | 0 .../effect/test/UndefinedOr.test.ts | 0 packages/effect/test/Version.test.ts | 17 + .../effect/test/ai/AiError.test.ts | 0 .../test/ai/AnthropicStructuredOutput.test.ts | 0 ...opicStructuredOutputRepresentation.test.ts | 0 .../effect/test/ai/Chat.test.ts | 0 .../effect/test/ai/DecisionModel.test.ts | 64 + .../effect/test/ai/EmbeddingModel.test.ts | 0 .../effect/test/ai/LanguageModel.test.ts | 41 + .../ai/LanguageModelRepresentation.test.ts | 0 .../ai/LanguageModelTrackerLifecycle.test.ts | 0 .../effect/test/ai/McpSchema.test.ts | 0 .../McpConformance/BaseProtocolTest.ts | 0 .../McpConformance/CompletionTest.ts | 0 .../McpConformance/ElicitationTest.ts | 0 .../McpServer/McpConformance/LifecycleTest.ts | 0 .../McpServer/McpConformance/LoggingTest.ts | 0 .../McpConformance/McpConformance.ts | 0 .../McpConformance/McpConformanceFixtures.ts | 0 .../McpServer/McpConformance/McpTestPeer.ts | 0 .../McpConformance/MultiRoundTripTest.ts | 0 .../McpServer/McpConformance/PromptsTest.ts | 0 .../McpServer/McpConformance/ResourcesTest.ts | 0 .../ai/McpServer/McpConformance/RootsTest.ts | 0 .../McpServer/McpConformance/SamplingTest.ts | 0 .../McpConformance/SubscriptionsTest.ts | 0 .../ai/McpServer/McpConformance/ToolsTest.ts | 0 .../McpConformance/TransportsTest.ts | 28 +- .../McpServer/McpConformance/UtilitiesTest.ts | 0 .../test/ai/McpServer/McpProtocol.test.ts | 0 .../test/ai/McpServer/McpServer.test.ts | 188 +- .../ai/McpServer/ProtocolAdapters.test.ts | 15 + .../ai/McpServer/TestUtils/McpHttpHarness.ts | 8 +- .../ai/McpServer/TestUtils/McpHttpResponse.ts | 0 .../ai/McpServer/TestUtils/McpServerLayer.ts | 4 +- .../ai/McpServer/TestUtils/McpStdioHarness.ts | 0 .../test/ai/McpServer/v2024_11_05.test.ts | 0 .../test/ai/McpServer/v2025_03_26.test.ts | 0 .../test/ai/McpServer/v2025_06_18.test.ts | 0 .../test/ai/McpServer/v2025_11_25.test.ts | 0 .../test/ai/McpServer/v2026_07_28.test.ts | 0 .../effect/test/ai/Model.test.ts | 0 .../test/ai/OpenAiStructuredOutput.test.ts | 0 ...enAiStructuredOutputRepresentation.test.ts | 0 .../effect/test/ai/Prompt.test.ts | 0 .../effect/test/ai/Response.test.ts | 0 .../effect/test/ai/ResponseIdTracker.test.ts | 0 .../effect/test/ai/Tokenizer.test.ts | 0 .../effect/test/ai/Tool.test.ts | 0 .../effect/test/ai/ToolRepresentation.test.ts | 0 .../effect/test/ai/utils.ts | 0 .../test/arbitrary/Arbitrary.array.test.ts | 0 .../test/arbitrary/Arbitrary.causes.test.ts | 80 + .../arbitrary/Arbitrary.composition.test.ts | 0 .../arbitrary/Arbitrary.configuration.test.ts | 0 .../effect/test/arbitrary/Arbitrary.test.ts | 98 + .../effect/test/cli/Ansi.test.ts | 0 .../effect/test/cli/Arguments.test.ts | 0 .../effect/test/cli/Command.test.ts | 0 .../effect/test/cli/Errors.test.ts | 0 .../effect/test/cli/Help.test.ts | 0 .../effect/test/cli/Lexer.test.ts | 0 .../effect/test/cli/LogLevel.test.ts | 0 .../effect/test/cli/Param.test.ts | 0 .../effect/test/cli/Primitive.test.ts | 0 .../effect/test/cli/Prompt.test.ts | 0 .../test/cli/completions/completions.test.ts | 0 .../test/cli/completions/descriptor.test.ts | 0 .../test/cli/fixtures/ComprehensiveCli.ts | 0 .../effect/test/cli/services/MockTerminal.ts | 0 .../effect/test/cli/services/TestActions.ts | 0 .../cluster/ClusterWorkflowEngine.test.ts | 0 .../effect/test/cluster/Entity.test.ts | 0 .../effect/test/cluster/EntityProxy.test.ts | 0 .../effect/test/cluster/Envelope.test.ts | 0 .../effect/test/cluster/K8sHttpClient.test.ts | 68 + .../test/cluster/MessageStorage.test.ts | 0 .../effect/test/cluster/ResourceMap.test.ts | 0 .../effect/test/cluster/ResourceRef.test.ts | 0 .../effect/test/cluster/RunnerServer.test.ts | 0 .../effect/test/cluster/RunnerStorage.test.ts | 0 .../effect/test/cluster/Runners.test.ts | 0 .../effect/test/cluster/Sharding.test.ts | 0 .../test/cluster/ShardingConfig.test.ts | 0 .../effect/test/cluster/TestEntity.ts | 0 .../effect/test/cluster/interruptors.test.ts | 0 .../test/devtools/DevToolsClient.test.ts | 0 .../effect/test/encoding/Base64.test.ts | 0 .../effect/test/encoding/Base64Url.test.ts | 0 .../test/encoding/EncodingError.test.ts | 0 .../effect/test/encoding/Hex.test.ts | 0 .../effect/test/encoding/Ini.test.ts | 0 .../effect/test/encoding/Ndjson.test.ts | 0 .../effect/test/encoding/SchemaBinary.test.ts | 0 .../effect/test/encoding/Sse.test.ts | 0 .../effect/test/encoding/Toml.test.ts | 12 + .../effect/test/encoding/Yaml.test.ts | 38 + .../effect/test/eventlog/EventJournal.test.ts | 0 .../effect/test/eventlog/EventLog.test.ts | 0 .../EventLogIdentityDerivation.test.ts | 0 .../test/eventlog/EventLogMessage.test.ts | 0 .../test/eventlog/EventLogRemote.test.ts | 0 .../EventLogServerUnencrypted.test.ts | 0 .../test/eventlog/EventLogSessionAuth.test.ts | 0 ...SqlEventLogServerUnencryptedStorageTest.ts | 339 + .../test/fixtures/migrator/0001_first.js | 0 .../effect/test/fixtures/text.txt | 0 .../effect/test/http/Cookies.test.ts | 0 .../effect/test/http/FetchHttpClient.test.ts | 0 .../http/FindMyWay/case-insensitive.test.ts | 0 .../http/FindMyWay/matching-order.test.ts | 0 .../test/http/FindMyWay/methods.test.ts | 0 .../http/FindMyWay/optional-params.test.ts | 0 .../http/FindMyWay/params-collisions.test.ts | 0 .../http/FindMyWay/path-params-match.test.ts | 0 .../test/http/FindMyWay/querystring.test.ts | 0 .../effect/test/http/FindMyWay/regex.test.ts | 0 .../effect/test/http/Headers.test.ts | 0 .../effect/test/http/HttpBody.test.ts | 0 .../effect/test/http/HttpClient.test.ts | 49 +- .../test/http/HttpClientRequest.test.ts | 0 .../effect/test/http/HttpCompression.test.ts | 0 .../effect/test/http/HttpEffect.test.ts | 0 .../effect/test/http/HttpMiddleware.test.ts | 78 +- .../effect/test/http/HttpPlatform.test.ts | 0 .../effect/test/http/HttpRouter.test.ts | 0 .../effect/test/http/HttpServer.test.ts | 0 .../effect/test/http/HttpServerError.test.ts | 0 .../test/http/HttpServerRequest.test.ts | 0 .../test/http/HttpServerRespondable.test.ts | 0 .../test/http/HttpServerResponse.test.ts | 0 .../effect/test/http/HttpStaticServer.test.ts | 0 .../effect/test/http/HttpStatus.test.ts | 0 .../effect/test/http/HttpTraceContext.test.ts | 0 .../effect/test/http/Mime.test.ts | 0 .../effect/test/http/Multipart.test.ts | 0 .../test/http/SchemaRepresentation.test.ts | 0 .../effect/test/http/Template.test.ts | 0 .../effect/test/http/Url.test.ts | 0 .../effect/test/http/UrlParams.test.ts | 0 .../effect/test/httpapi/HttpApi.test.ts | 0 .../test/httpapi/HttpApiBuilder.test.ts | 32 + .../effect/test/httpapi/HttpApiClient.test.ts | 0 .../test/httpapi/HttpApiDocumentation.test.ts | 0 .../test/httpapi/HttpApiEndpoint.test.ts | 0 .../effect/test/httpapi/HttpApiError.test.ts | 65 + .../effect/test/httpapi/HttpApiSchema.test.ts | 0 .../test/httpapi/HttpApiSecurity.test.ts | 0 .../effect/test/httpapi/OpenApi.test.ts | 0 .../httpapi/OpenApiRepresentation.test.ts | 0 .../effect/test/internal/Uuid.test.ts | 0 .../effect/test/net/IpInterface.test.ts | 0 .../effect/test/net/IpNetwork.test.ts | 0 .../effect/test/net/NetAddress.test.ts | 0 .../test/observability/OtlpConfig.test.ts | 5 +- .../test/observability/OtlpEnvHeaders.test.ts | 0 .../test/observability/OtlpExporter.test.ts | 27 +- .../test/observability/OtlpLogger.test.ts | 114 + .../test/observability/OtlpMetrics.test.ts | 181 +- .../test/observability/OtlpResource.test.ts | 43 +- .../observability/OtlpSerialization.test.ts | 59 + .../test/observability/OtlpTracer.test.ts | 35 + .../observability/PrometheusMetrics.test.ts | 0 .../test/persistence/KeyValueStore.test.ts | 0 .../test/persistence/KeyValueStoreTest.ts | 0 .../test/persistence/PersistedCache.test.ts | 0 .../test/persistence/PersistedCacheTest.ts | 0 .../test/persistence/PersistedQueue.test.ts | 0 .../test/persistence/PersistedQueueTest.ts | 0 .../test/persistence/RateLimiter.test.ts | 0 .../test/persistence/RateLimiterTest.ts | 0 .../effect/test/persistence/Redis.test.ts | 0 .../effect/test/persistence/SqlCleanupTest.ts | 0 .../effect/test/process/ChildProcess.test.ts | 0 .../test/process/ChildProcessSpawnerTest.ts | 0 .../process/fixtures/bash/no-permissions.sh | 0 .../fixtures/bash/parent-exits-early.sh | 0 .../process/fixtures/bash/spawn-children.sh | 0 .../test/process/fixtures/config/SHOUTING | 0 .../test/process/fixtures/config/integer | 0 .../process/fixtures/config/nested/config | 0 .../test/process/fixtures/config/secret | 0 .../test/reactivity/AsyncResult.test.ts | 0 .../effect/test/reactivity/Atom.test.ts | 47 + .../test/reactivity/AtomHttpApi.test.ts | 0 .../effect/test/reactivity/AtomRpc.test.ts | 0 .../effect/test/rpc/Rpc.test.ts | 0 .../effect/test/rpc/RpcClient.test.ts | 157 +- .../effect/test/rpc/RpcSerialization.test.ts | 0 .../effect/test/rpc/RpcServer.test.ts | 29 +- .../effect/test/schema/CodePoints.test.ts | 0 .../effect/test/schema/Graph.test.ts | 0 .../effect/test/schema/HMR.test.ts | 0 .../effect/test/schema/Schema.test.ts | 44 + .../test/schema/SchemaAOTCompiler.test.ts | 0 .../schema/SchemaAOTCompilerBuild.test.ts | 0 .../effect/test/schema/SchemaAST.test.ts | 0 .../test/schema/SchemaCompilerApi.test.ts | 0 .../test/schema/SchemaCompilerArray.test.ts | 0 .../schema/SchemaCompilerConcurrency.test.ts | 38 + .../schema/SchemaCompilerConstruction.test.ts | 0 .../schema/SchemaCompilerRegression.test.ts | 16 + .../test/schema/SchemaCompilerStartup.test.ts | 0 .../effect/test/schema/SchemaGetter.test.ts | 0 .../effect/test/schema/SchemaIssue.test.ts | 0 .../test/schema/SchemaJITCompiler.test.ts | 0 .../SchemaJITCompilerCheckedSources.test.ts | 0 .../SchemaJITCompilerExcessProperties.test.ts | 0 .../schema/SchemaJITCompilerFallback.test.ts | 0 .../effect/test/schema/SchemaParser.test.ts | 0 .../test/schema/SchemaTransformation.test.ts | 0 .../test/schema/TemplateLiteral.test.ts | 0 .../effect/test/schema/VariantSchema.test.ts | 0 .../effect/test/schema/fixtures/HMR-Class.ts | 0 .../test/schema/fixtures/HMR-sanity-check.ts | 0 .../effect/test/schema/fixtures/aot-build.ts | 0 .../test/schema/fixtures/aot-import-guard.ts | 0 .../effect/test/schema/fixtures/aot-runner.ts | 0 .../effect/test/schema/fixtures/aot.ts | 0 .../test/schema/fixtures/construction.ts | 0 .../test/schema/jsonSchemaRoundTrip.test.ts | 0 .../representation/builtInRevivers.test.ts | 0 .../schema/representation/fromJson.test.ts | 0 .../fromJsonMultiDocument.test.ts | 0 .../fromJsonSchemaDocument.test.ts | 0 .../fromJsonSchemaMultiDocument.test.ts | 0 .../representation/fromRepresentation.test.ts | 0 .../fromRepresentations.test.ts | 0 .../schema/representation/makeCode.test.ts | 0 .../schemaToJsonSchemaDocument.test.ts | 0 .../stringTreeRoundtrip.test.ts | 0 .../toCodeDocument.annotations.test.ts | 0 .../representation/toCodeDocument.test.ts | 0 .../test/schema/representation/toJson.test.ts | 0 .../toJsonMultiDocument.test.ts | 0 .../toJsonSchemaDocument.test.ts | 0 .../toJsonSchemaMultiDocument.test.ts | 0 .../representation/toMultiDocument.test.ts | 0 .../representation/toRepresentation.test.ts | 0 .../representation/toRepresentations.test.ts | 0 .../effect/test/schema/toCodec.test.ts | 0 .../test/schema/toDifferJsonPatch.test.ts | 0 .../effect/test/schema/toEquivalence.test.ts | 39 + .../effect/test/schema/toFormatter.test.ts | 0 .../effect/test/schema/toIso.test.ts | 0 .../test/schema/toJsonSchemaDocument.test.ts | 0 .../schema/toStandardJSONSchemaV1.test.ts | 30 + .../test/schema/toStandardSchemaV1.test.ts | 11 + .../effect/test/schema/v3-v4.test.ts | 0 .../effect/test/socket/Socket.test.ts | 0 .../effect/test/sql/SqlClient.test.ts | 0 .../effect/test/sql/SqlError.test.ts | 0 .../effect/test/sql/SqlResolver.test.ts | 0 .../effect/test/sql/SqlSchema.test.ts | 0 .../effect/test/sql/Statement.test.ts | 0 .../effect/test/testing/TestSchema.test.ts | 0 .../effect/test/utils/assert.ts | 0 .../effect/test/utils/chunkCoordination.ts | 0 .../effect/test/utils/counter.ts | 0 .../effect/test/utils/gc.ts | 0 .../effect/test/workers/Worker.test.ts | 0 .../effect/test/workers/WorkerError.test.ts | 0 .../effect/test/workflow/DurableQueue.test.ts | 0 .../effect/test/workflow/Workflow.test.ts | 0 .../test/workflow/WorkflowEngine.test.ts | 0 .../effect/tsconfig.json | 0 .../effect/typeperf/README.md | 0 .../effect/typeperf/compare.mjs | 0 .../effect/typeperf/compare/httpapi.mjs | 0 .../effect/typeperf/config.json | 0 .../effect/typeperf/run.mjs | 0 .../typeperf/suites/httpapi/baseline.ts | 0 .../fixtures/_endpoint-selection-types.ts | 0 .../httpapi/fixtures/_grouped-api-500.ts | 0 .../httpapi/fixtures/_grouped-api-types.ts | 0 .../fixtures/builder-endpoint-count-500.ts | 0 .../fixtures/builder-handle-all-count-500.ts | 0 ...uilder-handle-all-two-batches-count-500.ts | 0 .../fixtures/builder-handlers-count-100.ts | 0 .../builder-raw-handlers-count-100.ts | 0 .../fixtures/client-endpoint-count-500.ts | 0 .../fixtures/client-group-count-500.ts | 0 .../fixtures/client-groups-100x5-count-500.ts | 0 .../fixtures/client-methods-count-500.ts | 0 .../fixtures/client-top-level-count-500.ts | 0 .../fixtures/endpoint-class-count-500.ts | 0 .../httpapi/fixtures/endpoint-count-500.ts | 0 .../fixtures/group-endpoints-count-500.ts | 0 .../httpapi/fixtures/url-builder-count-500.ts | 0 .../url-builder-top-level-count-500.ts | 0 .../typeperf/suites/httpapi/thresholds.json | 0 .../effect/typeperf/suites/schema/baseline.ts | 0 .../typeperf/suites/schema/fixtures/array.ts | 0 .../typeperf/suites/schema/fixtures/brand.ts | 0 .../schema/fixtures/decode-unknown-effect.ts | 0 .../schema/fixtures/encode-unknown-effect.ts | 0 .../typeperf/suites/schema/fixtures/exit.ts | 0 .../suites/schema/fixtures/non-empty-array.ts | 0 .../typeperf/suites/schema/fixtures/record.ts | 0 .../suites/schema/fixtures/struct-mixed.ts | 0 .../schema/fixtures/struct-mutable-only.ts | 0 .../schema/fixtures/struct-optional-only.ts | 0 .../suites/schema/fixtures/struct-required.ts | 0 .../schema/fixtures/struct-with-rest.ts | 0 .../suites/schema/fixtures/tagged-union.ts | 0 .../fixtures/to-codec-array-from-single.ts | 0 .../suites/schema/fixtures/to-codec-json.ts | 0 .../schema/fixtures/to-codec-string-tree.ts | 0 .../suites/schema/fixtures/to-encoded.ts | 0 .../fixtures/to-standard-json-schema.ts | 0 .../suites/schema/fixtures/to-type.ts | 0 .../suites/schema/fixtures/tuple-with-rest.ts | 0 .../typeperf/suites/schema/fixtures/tuple.ts | 0 .../typeperf/suites/schema/fixtures/union.ts | 0 .../typeperf/suites/schema/thresholds.json | 0 .../effect/typetest/AiError.tst.ts | 0 .../effect/typetest/Array.tst.ts | 0 .../effect/typetest/Brand.tst.ts | 0 .../effect/typetest/ByteSize.tst.ts | 0 .../effect/typetest/Channel.tst.ts | 44 +- .../effect/typetest/ChannelSchema.tst.ts | 0 .../effect/typetest/Clock.tst.ts | 0 .../effect/typetest/Config.tst.ts | 0 .../effect/typetest/ConfigProvider.tst.ts | 0 .../effect/typetest/Context.tst.ts | 0 .../effect/typetest/Data.tst.ts | 0 .../effect/typetest/Effect.tst.ts | 42 +- .../effect/typetest/Effectable.tst.ts | 0 .../effect/typetest/Equivalence.tst.ts | 0 .../effect/typetest/Fiber.tst.ts | 0 .../effect/typetest/FileSystem.tst.ts | 0 .../effect/typetest/Function.tst.ts | 0 .../effect/typetest/Graph.tst.ts | 0 .../effect/typetest/Iterable.tst.ts | 0 .../effect/typetest/Layer.tst.ts | 0 .../effect/typetest/Match.tst.ts | 0 .../effect/typetest/MigratedSubpaths.tst.ts | 0 .../effect/typetest/Newtype.tst.ts | 0 .../effect/typetest/Optic.tst.ts | 0 .../effect/typetest/Option.tst.ts | 0 .../effect/typetest/Order.tst.ts | 0 .../effect/typetest/Predicate.tst.ts | 0 .../effect/typetest/Queue.tst.ts | 0 .../effect/typetest/Random.tst.ts | 0 .../effect/typetest/Record.tst.ts | 0 .../effect/typetest/Result.tst.ts | 0 .../effect/typetest/Schedule.tst.ts | 0 .../effect/typetest/Scope.tst.ts | 0 .../effect/typetest/Sink.tst.ts | 7 + .../effect/typetest/Stream.tst.ts | 0 .../effect/typetest/Struct.tst.ts | 0 .../effect/typetest/SynchronizedRef.tst.ts | 0 .../effect/typetest/TestSchema.tst.ts | 0 .../effect/typetest/Tuple.tst.ts | 0 .../effect/typetest/Types.tst.ts | 0 .../effect/typetest/VariantSchema.tst.ts | 0 .../effect/typetest/ai/DecisionModel.tst.ts | 0 .../effect/typetest/ai/LanguageModel.tst.ts | 0 .../effect/typetest/ai/McpServer.tst.ts | 50 +- .../effect/typetest/ai/Response.tst.ts | 0 .../effect/typetest/ai/Tool.tst.ts | 0 .../effect/typetest/ai/Toolkit.tst.ts | 0 .../typetest/arbitrary/Arbitrary.tst.ts | 0 .../effect/typetest/cli/Command.tst.ts | 0 .../effect/typetest/cli/Param.tst.ts | 0 .../effect/typetest/cli/Prompt.tst.ts | 0 .../typetest/cluster/EntityProxyServer.tst.ts | 0 .../typetest/cluster/K8sHttpClient.tst.ts | 0 .../effect/typetest/cluster/Reply.tst.ts | 0 .../effect/typetest/encoding/Ndjson.tst.ts | 0 .../effect/typetest/http/HttpClient.tst.ts | 0 .../effect/typetest/http/HttpPlatform.tst.ts | 0 .../effect/typetest/http/HttpRouter.tst.ts | 0 .../effect/typetest/http/HttpServer.tst.ts | 26 + .../effect/typetest/httpapi/HttpApi.tst.ts | 0 .../typetest/httpapi/HttpApiBuilder.tst.ts | 0 .../typetest/httpapi/HttpApiClient.tst.ts | 16 + .../typetest/httpapi/HttpApiEndpoint.tst.ts | 0 .../typetest/httpapi/HttpApiGroup.tst.ts | 0 .../typetest/httpapi/HttpApiMiddleware.tst.ts | 0 .../typetest/httpapi/HttpApiSchema.tst.ts | 0 .../httpapi/OpenApiRepresentation.tst.ts | 0 .../effect/typetest/net/IpInterface.tst.ts | 0 .../effect/typetest/net/IpNetwork.tst.ts | 0 .../effect/typetest/net/NetAddress.tst.ts | 0 .../typetest/reactivity/AsyncResult.tst.ts | 8 + .../effect/typetest/reactivity/Atom.tst.ts | 43 + .../typetest/reactivity/AtomHttpApi.tst.ts | 9 +- .../effect/typetest/reactivity/AtomRpc.tst.ts | 0 .../effect/typetest/rpc/Rpc.tst.ts | 0 .../effect/typetest/rpc/RpcClient.tst.ts | 0 .../effect/typetest/rpc/RpcGroup.tst.ts | 0 .../effect/typetest/rpc/RpcMessage.tst.ts | 0 .../effect/typetest/rpc/RpcMiddleware.tst.ts | 0 .../effect/typetest/rpc/RpcServer.tst.ts | 0 .../effect/typetest/schema/Array.tst.ts | 0 .../typetest/schema/FromJsonSchema.tst.ts | 0 .../effect/typetest/schema/Graph.tst.ts | 0 .../effect/typetest/schema/Literals.tst.ts | 0 .../effect/typetest/schema/Record.tst.ts | 0 .../effect/typetest/schema/Schema.tst.ts | 0 .../typetest/schema/SchemaAOTCompiler.tst.ts | 0 .../schema/SchemaAOTCompilerBuild.tst.ts | 0 .../schema/SchemaBuiltInRevivers.tst.ts | 0 .../typetest/schema/SchemaCompiler.tst.ts | 0 .../typetest/schema/SchemaGetter.tst.ts | 0 .../schema/SchemaJsonSchemaConsumer.tst.ts | 0 .../schema/SchemaRepresentation.tst.ts | 0 .../SchemaRepresentationCompilers.tst.ts | 0 .../schema/SchemaRepresentationReviver.tst.ts | 0 .../effect/typetest/schema/Struct.tst.ts | 0 .../effect/typetest/schema/Tuple.tst.ts | 0 .../effect/typetest/schema/Union.tst.ts | 5 + .../effect/typetest/schema/api.tst.ts | 0 .../effect/typetest/schema/options.tst.ts | 0 .../effect/typetest/schema/toIso.tst.ts | 0 .../effect/typetest/socket/Socket.tst.ts | 0 .../effect/typetest/sql/SqlSchema.tst.ts | 0 .../effect/typetest/workflow/Workflow.tst.ts | 0 packages/eslint-codegen-model/CHANGELOG.md | 1354 - .../examples/model-facades.ts | 318 - .../examples/workflow-facades.ts | 184 - packages/eslint-codegen-model/index.js | 3 - packages/eslint-codegen-model/package.json | 75 - packages/eslint-codegen-model/src/cli.ts | 638 - packages/eslint-codegen-model/src/index.ts | 4 - .../eslint-codegen-model/src/js-yaml.d.ts | 3 - .../eslint-codegen-model/src/normalise.ts | 38 - .../eslint-codegen-model/src/oxlint/index.ts | 8 - .../src/oxlint/rules/codegen.ts | 142 - .../src/presets/barrel.ts | 219 - .../eslint-codegen-model/src/presets/index.ts | 6 - .../eslint-codegen-model/src/presets/meta.ts | 48 - .../src/presets/model-facade.ts | 48 - .../eslint-codegen-model/src/presets/model.ts | 194 - .../src/shared/codegen-block.ts | 101 - .../src/shared/native-type-resolver.ts | 110 - .../src/shared/type-resolver.ts | 631 - packages/eslint-codegen-model/tsconfig.json | 14 - packages/eslint-shared-config/CHANGELOG.md | 535 - packages/eslint-shared-config/package.json | 59 - .../eslint-shared-config/src/dprint.jsonc | 36 - .../src/eslint.base.config.mjs | 114 - .../src/eslint.vue.config.mjs | 79 - .../eslint-shared-config/src/oxlintrc.json | 136 - .../src/plugin-effect-app.mjs | 91 - .../test/no-await-effect.test.mts | 204 - .../eslint-shared-config/vitest.config.ts | 9 - packages/infra/.madgerc | 7 - packages/infra/.npmignore | 14 - packages/infra/CHANGELOG.md | 15411 ----------- packages/infra/_check.sh | 3 - packages/infra/docs/cluster-storage.md | 26 - packages/infra/docs/workflow-engine.md | 262 - packages/infra/examples/query.ts | 138 - packages/infra/package.json | 111 - packages/infra/src/CUPS.ts | 196 - packages/infra/src/ClusterCosmos.ts | 1170 - packages/infra/src/ClusterServiceBus.ts | 244 - packages/infra/src/ContextProvider.ts | 165 - packages/infra/src/Emailer/Sendgrid.ts | 161 - packages/infra/src/Emailer/fake.ts | 18 - packages/infra/src/MainFiberSet.ts | 95 - packages/infra/src/QueueMaker/errors.ts | 32 - packages/infra/src/SQL.ts | 1 - packages/infra/src/SQL/Model.ts | 932 - packages/infra/src/Store/Cosmos.ts | 657 - packages/infra/src/Store/Cosmos/query.ts | 531 - packages/infra/src/Store/Disk.ts | 270 - packages/infra/src/Store/Memory.ts | 610 - packages/infra/src/Store/SQL.ts | 806 - packages/infra/src/Store/SQL/Pg.ts | 412 - packages/infra/src/Store/SQL/query.ts | 748 - packages/infra/src/Store/codeFilter.ts | 244 - packages/infra/src/Store/index.test.ts.bak | 112 - packages/infra/src/Store/index.ts | 48 - packages/infra/src/Store/jsonDocument.ts | 143 - packages/infra/src/Store/utils.ts | 345 - packages/infra/src/WorkflowEngineCosmos.ts | 749 - packages/infra/src/WorkflowEngineSqlite.ts | 813 - packages/infra/src/arbs.ts | 40 - packages/infra/src/codec.ts | 9 - packages/infra/src/cosmos-client.ts | 72 - packages/infra/src/errorReporter.ts | 100 - packages/infra/src/errors.ts | 1 - packages/infra/src/fileUtil.ts | 130 - packages/infra/src/index.ts | 2 - .../src/internal/RequestContextMiddleware.ts | 59 - packages/infra/src/internal/auth.ts | 272 - packages/infra/src/internal/events.ts | 77 - packages/infra/src/internal/health.ts | 5 - packages/infra/src/layerUtils.ts | 68 - packages/infra/src/logger.ts | 3 - packages/infra/src/logger/jsonLogger.ts | 35 - packages/infra/src/logger/logFmtLogger.ts | 18 - packages/infra/src/logger/shared.ts | 19 - packages/infra/src/middlewares.ts | 10 - packages/infra/src/mongo-client.ts | 25 - packages/infra/src/otel.ts | 177 - packages/infra/src/rateLimit.ts | 126 - packages/infra/src/redis-client.ts | 133 - packages/infra/src/reportError.ts | 30 - packages/infra/src/routing.ts | 823 - packages/infra/src/routing/middleware.ts | 4 - .../routing/middleware/RouterMiddleware.ts | 27 - .../src/routing/middleware/middleware.ts | 196 - packages/infra/src/routing/schema/jwt.ts | 21 - packages/infra/src/routing/tsort.ts | 56 - packages/infra/src/routing/utils.ts | 48 - packages/infra/src/test.ts | 30 - packages/infra/src/util.ts | 14 - packages/infra/src/vitest.ts | 2 - packages/infra/test/arbs-magnitude.test.ts | 33 - packages/infra/test/arbs.test.ts | 41 - packages/infra/test/auth.test.ts | 101 - packages/infra/test/cluster-cosmos.test.ts | 634 - .../infra/test/cluster-servicebus.test.ts | 180 - packages/infra/test/cluster-sqlite.test.ts | 207 - packages/infra/test/contextProvider.test.ts | 161 - packages/infra/test/controller.test.ts | 425 - .../test/cosmos-container-throughput.test.ts | 80 - packages/infra/test/cosmos-query.test.ts | 368 - packages/infra/test/fixtures.ts | 139 - packages/infra/test/json-lower.test.ts | 129 - packages/infra/test/layerUtils.test.ts | 21 - packages/infra/test/query.test.ts | 2436 -- packages/infra/test/rawQuery.test.ts | 851 - packages/infra/test/repository-ext.test.ts | 456 - .../test/repository-legacy-document.test.ts | 394 - packages/infra/test/requires.test.ts | 182 - packages/infra/test/router-generator.test.ts | 187 - .../test/routing-interruptibility.test.ts | 66 - .../test/rpc-context-map-streaming.test.ts | 265 - .../infra/test/rpc-e2e-invalidation.test.ts | 421 - .../infra/test/rpc-multi-middleware.test.ts | 213 - .../infra/test/rpc-stream-fullstack.test.ts | 307 - packages/infra/test/sql-store.test.ts | 1931 -- packages/infra/test/validateSample.test.ts | 265 - .../infra/test/workflow-engine-cosmos.test.ts | 345 - .../infra/test/workflow-engine-sqlite.test.ts | 297 - packages/infra/tsconfig.examples.json | 15 - packages/infra/tsconfig.json | 12 - packages/infra/tsconfig.src.json | 38 - packages/infra/tsconfig.test.json | 33 - packages/infra/types/modules.d.ts | 0 packages/infra/vitest.config.ts | 5 - .../opentelemetry/CHANGELOG.md | 20 + .../opentelemetry/LICENSE | 0 .../opentelemetry/README.md | 0 .../opentelemetry/package.json | 2 +- .../opentelemetry/src/NodeSdk.ts | 0 .../opentelemetry/src/OtelLogger.ts | 25 +- .../opentelemetry/src/OtelMetrics.ts | 0 .../opentelemetry/src/OtelTracer.ts | 68 +- .../opentelemetry/src/Resource.ts | 32 +- .../opentelemetry/src/WebSdk.ts | 0 .../opentelemetry/src/index.ts | 1 + .../opentelemetry/src/internal/attributes.ts | 10 + .../opentelemetry/src/internal/metrics.ts | 15 +- .../opentelemetry/src/internal/utilities.ts | 0 .../opentelemetry/test/OtelLogger.test.ts | 68 +- .../opentelemetry/test/OtelMetrics.test.ts | 43 + .../opentelemetry/test/OtelTracer.test.ts | 127 +- packages/opentelemetry/test/Resource.test.ts | 24 + .../opentelemetry/tsconfig.json | 0 .../platform/browser/CHANGELOG.md | 8 + .../platform/browser/LICENSE | 0 .../platform/browser/README.md | 0 .../platform/browser/package.json | 2 +- .../platform/browser/src/BrowserCrypto.ts | 3 + .../platform/browser/src/BrowserHttpClient.ts | 9 + .../browser/src/BrowserKeyValueStore.ts | 4 + .../browser/src/BrowserPersistence.ts | 3 + .../platform/browser/src/BrowserRuntime.ts | 2 + .../platform/browser/src/BrowserSocket.ts | 3 + .../platform/browser/src/BrowserStream.ts | 3 + .../platform/browser/src/BrowserWorker.ts | 3 + .../browser/src/BrowserWorkerRunner.ts | 4 + .../platform/browser/src/Clipboard.ts | 6 + .../platform/browser/src/Geolocation.ts | 10 + .../platform/browser/src/IndexedDb.ts | 7 + .../platform/browser/src/IndexedDbDatabase.ts | 11 + .../browser/src/IndexedDbQueryBuilder.ts | 9 + .../platform/browser/src/IndexedDbTable.ts | 14 + .../platform/browser/src/IndexedDbVersion.ts | 8 + .../platform/browser/src/Permissions.ts | 8 + .../platform/browser/src/index.ts | 18 + .../browser/test/BrowserCrypto.test.ts | 0 .../browser/test/BrowserHttpClient.test.ts | 0 .../browser/test/BrowserKeyValueStore.test.ts | 0 .../browser/test/BrowserPersistence.test.ts | 0 .../BrowserPersistencePersistedCache.test.ts | 0 .../browser/test/BrowserWorkerRunner.test.ts | 0 .../browser/test/IndexedDbDatabase.test.ts | 0 .../test/IndexedDbQueryBuilder.test.ts | 0 .../browser/test/IndexedDbTable.test.ts | 0 .../browser/test/IndexedDbVersion.test.ts | 0 .../platform/browser/test/Permissions.test.ts | 0 .../platform/browser/test/RpcWorker.test.ts | 0 .../platform/browser/test/fixtures/rpc-e2e.ts | 0 .../browser/test/fixtures/rpc-schemas.ts | 0 .../browser/test/fixtures/rpc-worker.ts | 0 .../platform/browser/tsconfig.json | 0 .../platform/browser/vitest.setup.ts | 0 .../platform/bun/CHANGELOG.md | 11 + .../platform/bun/LICENSE | 0 .../platform/bun/README.md | 0 .../platform/bun/package.json | 2 +- .../bun/src/BunChildProcessSpawner.ts | 1 + .../platform/bun/src/BunClusterHttp.ts | 4 + .../platform/bun/src/BunClusterSocket.ts | 5 + .../platform/bun/src/BunCrypto.ts | 2 + .../platform/bun/src/BunFileSystem.ts | 2 + .../platform/bun/src/BunHttpClient.ts | 1 + .../platform/bun/src/BunHttpPlatform.ts | 2 + .../platform/bun/src/BunHttpServer.ts | 20 +- .../platform/bun/src/BunHttpServerRequest.ts | 2 + .../platform/bun/src/BunMultipart.ts | 3 + .../platform/bun/src/BunPath.ts | 4 + .../platform/bun/src/BunRedis.ts | 0 .../platform/bun/src/BunRuntime.ts | 2 + .../platform/bun/src/BunServices.ts | 3 + .../platform/bun/src/BunSink.ts | 1 + .../platform/bun/src/BunSocket.ts | 3 + .../platform/bun/src/BunSocketServer.ts | 1 + .../platform/bun/src/BunStdio.ts | 2 + .../platform/bun/src/BunStream.ts | 2 + .../platform/bun/src/BunTerminal.ts | 3 + .../platform/bun/src/BunWorker.ts | 3 + .../platform/bun/src/BunWorkerRunner.ts | 2 + .../platform/bun/src/index.ts | 22 + .../platform/bun/test/BunClusterHttp.test.ts | 0 .../bun/test/BunHttpCompression.test.ts | 0 .../platform/bun/test/BunHttpPlatform.test.ts | 0 .../platform/bun/test/BunHttpServer.test.ts | 126 +- .../platform/bun/test/BunMultipart.test.ts | 0 .../platform/bun/test/BunStream.test.ts | 0 .../platform/bun/tsconfig.json | 0 .../platform/deno/CHANGELOG.md | 11 + .../platform/deno/LICENSE | 0 .../platform/deno/README.md | 0 .../platform/deno/package.json | 4 +- .../deno/src/DenoChildProcessSpawner.ts | 4 + .../platform/deno/src/DenoClusterHttp.ts | 4 + .../platform/deno/src/DenoClusterSocket.ts | 5 + .../platform/deno/src/DenoCrypto.ts | 3 + .../platform/deno/src/DenoFileSystem.ts | 2 + .../platform/deno/src/DenoHttpClient.ts | 1 + .../platform/deno/src/DenoHttpPlatform.ts | 3 + .../platform/deno/src/DenoHttpServer.ts | 21 +- .../deno/src/DenoHttpServerRequest.ts | 2 + .../platform/deno/src/DenoKeyValueStore.ts | 3 + .../platform/deno/src/DenoMultipart.ts | 3 + .../platform/deno/src/DenoPath.ts | 4 + .../platform/deno/src/DenoRedis.ts | 0 .../platform/deno/src/DenoRuntime.ts | 2 + .../platform/deno/src/DenoServices.ts | 3 + .../platform/deno/src/DenoSocket.ts | 10 + .../platform/deno/src/DenoSocketServer.ts | 1 + .../platform/deno/src/DenoStdio.ts | 2 + .../platform/deno/src/DenoTerminal.ts | 3 + .../platform/deno/src/DenoWorker.ts | 3 + .../platform/deno/src/DenoWorkerRunner.ts | 4 + .../platform/deno/src/index.ts | 21 + .../platform/deno/src/internal/error.ts | 0 .../deno/test/DenoChildProcessSpawner.test.ts | 0 .../platform/deno/test/DenoCrypto.test.ts | 0 .../platform/deno/test/DenoFileSystem.test.ts | 0 .../deno/test/DenoHttpCompression.test.ts | 0 .../deno/test/DenoHttpPlatform.test.ts | 0 .../platform/deno/test/DenoHttpServer.test.ts | 38 +- .../deno/test/DenoKeyValueStore.test.ts | 0 .../platform/deno/test/DenoPath.test.ts | 0 .../deno/test/DenoRedis.integration.test.ts | 0 .../platform/deno/test/DenoSocket.test.ts | 0 .../deno/test/DenoSocketServer.test.ts | 0 .../platform/deno/test/DenoStdio.test.ts | 0 .../platform/deno/test/DenoTerminal.test.ts | 0 .../deno/test/DenoWorkerRunner.test.ts | 0 .../platform/deno/test/RpcWorker.test.ts | 0 .../deno/test/cluster/SocketRunner.test.ts | 0 .../deno/test/fixtures/deno-terminal.ts | 0 .../platform/deno/test/fixtures/rpc-e2e.ts | 0 .../deno/test/fixtures/rpc-schemas.ts | 0 .../platform/deno/test/fixtures/rpc-worker.ts | 0 .../platform/deno/test/fixtures/text.txt | 0 .../platform/deno/test/fixtures/tls/ca.pem | 0 .../platform/deno/test/fixtures/tls/cert.pem | 0 .../platform/deno/test/fixtures/tls/key.pem | 0 .../platform/deno/test/internal/error.test.ts | 0 .../platform/deno/tsconfig.json | 0 .../platform/node-shared/CHANGELOG.md | 10 + .../platform/node-shared/LICENSE | 0 .../platform/node-shared/README.md | 0 .../platform/node-shared/benchmark/Socket.ts | 0 .../platform/node-shared/package.json | 6 +- .../src/NodeChildProcessSpawner.ts | 4 + .../node-shared/src/NodeClusterSocket.ts | 3 + .../platform/node-shared/src/NodeCrypto.ts | 3 + .../node-shared/src/NodeFileSystem.ts | 2 + .../node-shared/src/NodeHttpCompression.ts | 5 + .../platform/node-shared/src/NodePath.ts | 4 + .../platform/node-shared/src/NodeRuntime.ts | 2 + .../platform/node-shared/src/NodeSink.ts | 4 + .../platform/node-shared/src/NodeSocket.ts | 9 + .../node-shared/src/NodeSocketServer.ts | 6 + .../platform/node-shared/src/NodeStdio.ts | 2 + .../platform/node-shared/src/NodeStream.ts | 11 + .../platform/node-shared/src/NodeTerminal.ts | 3 + .../platform/node-shared/src/index.ts | 14 + .../src/internal/nodeChildProcessSpawner.ts | 0 .../node-shared/src/internal/utils.ts | 0 .../test/HttpPlatform.test-utils.ts | 0 .../test/NodeChildProcessSpawner.test.ts | 0 .../node-shared/test/NodeFileSystem.test.ts | 0 .../node-shared/test/NodePath.test.ts | 0 .../node-shared/test/NodeSink.test.ts | 0 .../node-shared/test/NodeStdio.test.ts | 0 .../node-shared/test/NodeStream.test.ts | 0 .../node-shared/test/NodeTerminal.test.ts | 0 .../test/fixtures/helloworld.tar.gz | Bin .../test/fixtures/node-terminal.ts | 0 .../test/fixtures/process-group.ts | 0 .../platform/node-shared/tsconfig.json | 0 .../platform/node/CHANGELOG.md | 13 + .../platform/node/LICENSE | 0 .../platform/node/README.md | 0 .../platform/node/package.json | 12 +- .../node/src/NodeChildProcessSpawner.ts | 1 + .../platform/node/src/NodeClusterHttp.ts | 4 + .../platform/node/src/NodeClusterSocket.ts | 5 + .../platform/node/src/NodeCrypto.ts | 2 + .../platform/node/src/NodeFileSystem.ts | 2 + .../platform/node/src/NodeHttpClient.ts | 11 + .../node/src/NodeHttpIncomingMessage.ts | 2 + .../platform/node/src/NodeHttpPlatform.ts | 3 + .../platform/node/src/NodeHttpServer.ts | 44 +- .../node/src/NodeHttpServerRequest.ts | 3 + .../platform/node/src/NodeMultipart.ts | 4 + .../platform/node/src/NodeMultipartParser.ts | 8 + .../platform/node/src/NodePath.ts | 4 + .../platform/node/src/NodeRedis.ts | 0 .../platform/node/src/NodeRuntime.ts | 2 + .../platform/node/src/NodeServices.ts | 3 + .../platform/node/src/NodeSink.ts | 1 + .../platform/node/src/NodeSocket.ts | 4 + .../platform/node/src/NodeSocketServer.ts | 1 + .../platform/node/src/NodeStdio.ts | 2 + .../platform/node/src/NodeStream.ts | 1 + .../platform/node/src/NodeTerminal.ts | 3 + .../platform/node/src/NodeWorker.ts | 3 + .../platform/node/src/NodeWorkerRunner.ts | 2 + .../platform/node/src/Undici.ts | 0 .../platform/node/src/index.ts | 24 + .../platform/node/test/HttpApi.test.ts | 0 .../node/test/HttpStaticServer.test.ts | 0 .../test/HttpStaticServerConditional.test.ts | 0 .../platform/node/test/KeyValueStore.test.ts | 0 .../node/test/MultipartParser.test.ts | 0 .../platform/node/test/NodeCrypto.test.ts | 0 .../platform/node/test/NodeHttpClient.test.ts | 0 .../node/test/NodeHttpCompression.test.ts | 0 .../node/test/NodeHttpPlatform.test.ts | 0 .../platform/node/test/NodeHttpServer.test.ts | 165 +- .../node/test/NodeRedis.integration.test.ts | 0 .../platform/node/test/NodeSocket.test.ts | 0 .../node/test/NodeWorkerRunner.test.ts | 0 .../platform/node/test/OpenApi.test.ts | 0 .../platform/node/test/RpcServer.test.ts | 91 + .../test/__snapshots__/HttpApi.test.ts.snap | 0 .../cluster-integration/ClusterCron.test.ts | 0 .../test/cluster-integration/Entity.test.ts | 0 .../test/cluster-integration/Locks.test.ts | 0 .../cluster-integration/Persistence.test.ts | 0 .../node/test/cluster-integration/README.md | 0 .../cluster-integration/Residency.test.ts | 0 .../test/cluster-integration/Smoke.test.ts | 0 .../StreamDisconnect.test.ts | 0 .../cluster-integration/Transport.test.ts | 0 .../test/cluster-integration/Workflow.test.ts | 0 .../test/cluster-integration/globalSetup.ts | 0 .../node/test/cluster-integration/harness.ts | 0 .../node/test/cluster/MessageStorageTest.ts | 0 .../node/test/cluster/SocketRunner.test.ts | 0 .../SqlMessageStorage.integration.test.ts | 0 .../SqlRunnerStorage.integration.test.ts | 0 .../fixtures/http-static-server-outside.txt | 0 .../http-static-server/conditional.txt | 0 .../http-static-server/custom/home.html | 0 .../fixtures/http-static-server/file.binx | 0 .../http-static-server/guide/index.html | 0 .../fixtures/http-static-server/hello.txt | 0 .../fixtures/http-static-server/index.html | 0 .../fixtures/http-static-server/range.txt | 0 .../node/test/fixtures/mysql2-utils.ts | 0 .../platform/node/test/fixtures/pg-utils.ts | 0 .../platform/node/test/fixtures/rpc-e2e.ts | 0 .../node/test/fixtures/rpc-schemas.ts | 0 .../platform/node/test/fixtures/text.txt | 0 .../platform/node/test/fixtures/tls/cert.pem | 0 .../platform/node/test/fixtures/tls/key.pem | 0 .../node/test/fixtures/worker-runner.ts | 0 .../platform/node/tsconfig.json | 0 .../sql/clickhouse/CHANGELOG.md | 9 + .../sql/clickhouse/LICENSE | 0 .../sql/clickhouse/README.md | 0 .../sql/clickhouse/package.json | 2 +- .../sql/clickhouse/src/ClickhouseClient.ts | 0 .../sql/clickhouse/src/ClickhouseMigrator.ts | 3 + .../sql/clickhouse/src/index.ts | 2 + .../sql/clickhouse/test/Client.test.ts | 0 .../test/SqlErrorClassification.test.ts | 0 .../sql/clickhouse/tsconfig.json | 0 .../packages => packages}/sql/d1/CHANGELOG.md | 17 + .../packages => packages}/sql/d1/LICENSE | 0 .../packages => packages}/sql/d1/README.md | 0 .../packages => packages}/sql/d1/package.json | 4 +- .../sql/d1/src/D1Client.ts | 14 +- .../packages => packages}/sql/d1/src/index.ts | 1 + .../sql/d1/test/Client.test.ts | 24 +- .../sql/d1/test/Resolver.test.ts | 0 .../sql/d1/test/utils.ts | 0 .../sql/d1/tsconfig.json | 0 .../sql/libsql/CHANGELOG.md | 8 + .../packages => packages}/sql/libsql/LICENSE | 0 .../sql/libsql/README.md | 0 .../sql/libsql/package.json | 4 +- .../sql/libsql/src/LibsqlClient.ts | 10 + .../sql/libsql/src/LibsqlMigrator.ts | 3 + .../sql/libsql/src/index.ts | 3 + .../libsql/test/Client.integration.test.ts | 0 .../libsql/test/Resolver.integration.test.ts | 0 .../sql/libsql/test/util.ts | 0 .../sql/libsql/tsconfig.json | 0 .../sql/mssql/CHANGELOG.md | 10 + .../packages => packages}/sql/mssql/LICENSE | 0 .../packages => packages}/sql/mssql/README.md | 0 .../sql/mssql/package.json | 6 +- .../sql/mssql/src/MssqlClient.ts | 10 + .../sql/mssql/src/MssqlMigrator.ts | 3 + .../sql/mssql/src/Parameter.ts | 0 .../sql/mssql/src/Procedure.ts | 0 .../sql/mssql/src/index.ts | 4 + .../sql/mssql/test/Binary.test.ts | 0 .../sql/mssql/test/Client.test.ts | 0 .../test/Persistence.integration.test.ts | 0 .../mssql/test/SqlErrorClassification.test.ts | 0 .../sql/mssql/test/utils.ts | 0 .../sql/mssql/tsconfig.json | 0 .../sql/mysql2/CHANGELOG.md | 10 + .../packages => packages}/sql/mysql2/LICENSE | 0 .../sql/mysql2/README.md | 0 .../sql/mysql2/package.json | 6 +- .../sql/mysql2/src/MysqlClient.ts | 10 + .../sql/mysql2/src/MysqlMigrator.ts | 3 + .../sql/mysql2/src/index.ts | 3 + .../mysql2/test/Client.integration.test.ts | 0 .../test/KeyValueStore.integration.test.ts | 0 .../sql/mysql2/test/Model.integration.test.ts | 0 .../test/MysqlClient.integration.test.ts | 0 .../test/Persistence.integration.test.ts | 0 .../test/SqlErrorClassification.test.ts | 0 ...ntLogServerUnencrypted.integration.test.ts | 0 .../sql/mysql2/test/utils.ts | 0 .../sql/mysql2/tsconfig.json | 0 .../packages => packages}/sql/pg/CHANGELOG.md | 14 + .../packages => packages}/sql/pg/LICENSE | 0 .../packages => packages}/sql/pg/README.md | 0 .../sql/pg/benchmark/PgClient.ts | 0 .../sql/pg/benchmark/PgCodec.ts | 0 .../sql/pg/benchmark/README.md | 0 .../packages => packages}/sql/pg/package.json | 6 +- .../sql/pg/src/PgAuth.ts | 10 + .../sql/pg/src/PgClient.ts | 14 + .../sql/pg/src/PgConnection.ts | 25 +- .../sql/pg/src/PgMigrator.ts | 3 + .../sql/pg/src/PgPool.ts | 7 + .../sql/pg/src/PgProtocol.ts | 73 + .../sql/pg/src/PgTypes.ts | 124 +- .../packages => packages}/sql/pg/src/index.ts | 8 + .../sql/pg/src/internal/connection.ts | 0 .../sql/pg/src/internal/password.ts | 0 .../sql/pg/src/internal/sqlError.ts | 0 .../sql/pg/test/Client.integration.test.ts | 0 .../pg/test/KeyValueStore.integration.test.ts | 0 .../pg/test/Persistence.integration.test.ts | 0 .../sql/pg/test/PgAuth.test.ts | 0 .../pg/test/PgConnection.in-process.test.ts | 0 .../pg/test/PgConnection.integration.test.ts | 29 + .../sql/pg/test/PgConnection.test.ts | 0 .../sql/pg/test/PgMigrator.test.ts | 0 .../sql/pg/test/PgPool.in-process.test.ts | 0 .../sql/pg/test/PgPool.integration.test.ts | 191 +- .../sql/pg/test/PgProtocol.test.ts | 0 .../sql/pg/test/PgTypes.test.ts | 77 + .../pg/test/SqlErrorClassification.test.ts | 0 .../test/SqlEventJournal.integration.test.ts | 58 + ...ntLogServerUnencrypted.integration.test.ts | 132 + .../StartupParameters.integration.test.ts | 0 .../sql/pg/test/TransactionAcquire.test.ts | 0 .../sql/pg/test/fixtures/goldens.ts | 0 .../sql/pg/test/fixtures/regenerate.ts | 0 .../sql/pg/test/utils.ts | 0 .../sql/pg/tsconfig.json | 0 .../sql/pglite/CHANGELOG.md | 8 + .../packages => packages}/sql/pglite/LICENSE | 0 .../sql/pglite/README.md | 0 .../sql/pglite/package.json | 2 +- .../sql/pglite/src/PgliteClient.ts | 0 .../sql/pglite/src/PgliteMigrator.ts | 3 + .../sql/pglite/src/index.ts | 2 + .../sql/pglite/test/Client.test.ts | 0 .../sql/pglite/test/Json.test.ts | 0 .../sql/pglite/test/Migrator.test.ts | 0 .../sql/pglite/test/PersistedQueue.test.ts | 0 .../test/SqlErrorClassification.test.ts | 0 .../sql/pglite/test/Transaction.test.ts | 0 .../sql/pglite/tsconfig.json | 0 .../sql/sqlite-bun/CHANGELOG.md | 8 + .../sql/sqlite-bun/LICENSE | 0 .../sql/sqlite-bun/README.md | 0 .../sql/sqlite-bun/package.json | 2 +- .../sql/sqlite-bun/src/SqliteClient.ts | 9 + .../sql/sqlite-bun/src/SqliteMigrator.ts | 3 + .../sql/sqlite-bun}/src/index.ts | 2 + .../sql/sqlite-bun/test/Client.test.ts | 0 .../sql/sqlite-bun/tsconfig.json | 0 .../sql/sqlite-do/CHANGELOG.md | 8 + .../sql/sqlite-do/LICENSE | 0 .../sql/sqlite-do/README.md | 0 .../sql/sqlite-do/package.json | 4 +- .../sql/sqlite-do/src/SqliteClient.ts | 0 .../sql/sqlite-do/src/SqliteMigrator.ts | 3 + .../sql/sqlite-do}/src/index.ts | 3 + .../sql/sqlite-do/test/Client.test.ts | 0 .../sql/sqlite-do/test/Miniflare.test.ts | 0 .../sqlite-do/test/fixtures/transaction.ts | 0 .../sql/sqlite-do/tsconfig.json | 0 .../sql/sqlite-node/CHANGELOG.md | 8 + .../sql/sqlite-node/LICENSE | 0 .../sql/sqlite-node/README.md | 0 .../sql/sqlite-node/package.json | 4 +- .../sql/sqlite-node/src/SqliteClient.ts | 10 + .../sql/sqlite-node/src/SqliteMigrator.ts | 3 + .../sql/sqlite-node}/src/index.ts | 3 + .../sql/sqlite-node/test/Client.test.ts | 0 .../sqlite-node/test/KeyValueStore.test.ts | 0 .../sql/sqlite-node/test/Persistence.test.ts | 0 .../sql/sqlite-node/test/Resolver.test.ts | 0 .../sqlite-node/test/SqlEventJournal.test.ts | 0 .../test/SqlEventLogServerEncrypted.test.ts | 0 .../test/SqlEventLogServerUnencrypted.test.ts | 0 .../sqlite-node/test/SqlModelServices.test.ts | 0 .../sqlite-node/test/SqliteMigrator.test.ts | 0 .../sql/sqlite-node/tsconfig.json | 0 .../sql/sqlite-react-native/CHANGELOG.md | 8 + .../sql/sqlite-react-native/LICENSE | 0 .../sql/sqlite-react-native/README.md | 0 .../sql/sqlite-react-native/package.json | 2 +- .../sqlite-react-native/src/SqliteClient.ts | 11 + .../sqlite-react-native/src/SqliteMigrator.ts | 3 + .../sql/sqlite-react-native}/src/index.ts | 3 + .../sqlite-react-native/test/Client.test.ts | 0 .../sql/sqlite-react-native/tsconfig.json | 0 .../sql/sqlite-wasm/CHANGELOG.md | 8 + .../sql/sqlite-wasm/LICENSE | 0 .../sql/sqlite-wasm/README.md | 0 .../sql/sqlite-wasm/package.json | 2 +- .../sql/sqlite-wasm/src/OpfsWorker.ts | 3 + .../sql/sqlite-wasm/src/SqliteClient.ts | 15 + .../sql/sqlite-wasm/src/SqliteMigrator.ts | 3 + .../sql/sqlite-wasm/src/index.ts | 3 + .../sqlite-wasm/src/internal/opfsWorker.ts | 0 .../sql/sqlite-wasm/src/sqlite-wasm.d.ts | 1 + .../sql/sqlite-wasm/test/Client.test.ts | 0 .../sql/sqlite-wasm/test/Memory.test.ts | 0 .../sql/sqlite-wasm/tsconfig.json | 0 .../tools/ai-codegen/codegen.schema.json | 0 .../tools/ai-codegen/package.json | 2 +- .../tools/ai-codegen/src/Config.ts | 0 .../tools/ai-codegen/src/Discovery.ts | 0 .../tools/ai-codegen/src/Generator.ts | 0 .../tools/ai-codegen/src/Glob.ts | 0 .../tools/ai-codegen/src/PostProcess.ts | 0 .../tools/ai-codegen/src/SpecFetcher.ts | 0 .../tools/ai-codegen/src/bin.ts | 0 .../tools/ai-codegen/src/main.ts | 0 .../tools/ai-codegen/test/Generator.test.ts | 0 .../test/OpenRouterTranscriptions.test.ts | 0 .../tools/ai-codegen/tsconfig.json | 0 .../tools/ai-docgen/package.json | 2 +- .../tools/ai-docgen/src/Glob.ts | 0 .../tools/ai-docgen/src/main.ts | 0 .../tools/ai-docgen/tsconfig.json | 0 .../tools/api-diff/README.md | 0 .../tools/api-diff/package.json | 4 +- .../tools/api-diff/src/Annotations.ts | 0 .../tools/api-diff/src/ApiDiff.ts | 0 .../tools/api-diff/src/Cli.ts | 0 .../tools/api-diff/src/Diff.ts | 0 .../tools/api-diff/src/Discovery.ts | 0 .../tools/api-diff/src/Error.ts | 0 .../tools/api-diff/src/Json.ts | 0 .../tools/api-diff/src/MigrationDoc.ts | 0 .../tools/api-diff/src/Model.ts | 0 .../tools/api-diff/src/Report.ts | 0 .../tools/api-diff/src/Snapshot.ts | 0 .../tools/api-diff/src/Worktrees.ts | 0 .../tools/api-diff/src/bin.ts | 0 .../tools/api-diff/test/Annotations.test.ts | 0 .../tools/api-diff/test/Cli.test.ts | 0 .../tools/api-diff/test/Diff.test.ts | 0 .../tools/api-diff/test/Discovery.test.ts | 0 .../tools/api-diff/test/Error.test.ts | 0 .../tools/api-diff/test/MigrationDoc.test.ts | 0 .../tools/api-diff/test/Report.test.ts | 0 .../tools/api-diff/test/Snapshot.test.ts | 0 .../tools/api-diff/test/Worktrees.test.ts | 0 .../tools/api-diff/test/utils.ts | 0 .../tools/api-diff/tsconfig.json | 0 .../tools/bundle/README.md | 0 .../bundle/fixtures/arbitrary-combinators.ts | 0 .../tools/bundle/fixtures/basic.ts | 0 .../tools/bundle/fixtures/batching.ts | 0 .../tools/bundle/fixtures/brand.ts | 0 .../tools/bundle/fixtures/cache.ts | 0 .../tools/bundle/fixtures/config.ts | 0 .../tools/bundle/fixtures/differ.ts | 0 .../tools/bundle/fixtures/http-client.ts | 0 .../tools/bundle/fixtures/http-router.ts | 0 .../tools/bundle/fixtures/logger.ts | 0 .../tools/bundle/fixtures/metric.ts | 0 .../tools/bundle/fixtures/optic.ts | 0 .../tools/bundle/fixtures/pubsub.ts | 0 .../tools/bundle/fixtures/queue.ts | 0 .../tools/bundle/fixtures/schedule.ts | 0 .../bundle/fixtures/schema-bigdecimal.ts | 0 .../tools/bundle/fixtures/schema-binary.ts | 0 .../tools/bundle/fixtures/schema-class.ts | 0 .../fixtures/schema-fromJsonSchemaDocument.ts | 0 .../schema-representation-roundtrip.ts | 0 .../fixtures/schema-string-transformation.ts | 0 .../tools/bundle/fixtures/schema-string.ts | 0 .../fixtures/schema-template-literal.ts | 0 .../bundle/fixtures/schema-toArbitrary.ts | 0 .../bundle/fixtures/schema-toCodeDocument.ts | 0 .../bundle/fixtures/schema-toCodecJson.ts | 0 .../bundle/fixtures/schema-toEquivalence.ts | 0 .../bundle/fixtures/schema-toFormatter.ts | 0 .../fixtures/schema-toJsonSchemaDocument.ts | 0 .../fixtures/schema-toRepresentation.ts | 0 .../tools/bundle/fixtures/schema.ts | 0 .../tools/bundle/fixtures/stm.ts | 0 .../tools/bundle/fixtures/stream.ts | 0 .../tools/bundle/package.json | 4 +- .../tools/bundle/src/Cli.ts | 0 .../tools/bundle/src/Fixtures.ts | 0 .../tools/bundle/src/Plugins.ts | 0 .../tools/bundle/src/Reporter.ts | 0 .../tools/bundle/src/Rollup.ts | 0 .../tools/bundle/src/bin.ts | 0 .../bundle/test/HttpRouterEsbuild.test.ts | 0 .../tools/bundle/test/Plugins.test.ts | 0 .../tools/bundle/tsconfig.fixtures.json | 0 .../tools/bundle/tsconfig.json | 0 .../tools/bundle/tsconfig.src.json | 0 .../tools/docgen/CHANGELOG.md | 9 + .../tools/docgen/LICENSE | 0 .../tools/docgen/README.md | 0 .../tools/docgen/package.json | 8 +- .../tools/docgen/schema.json | 0 .../tools/docgen/src/CLI.ts | 0 .../tools/docgen/src/Checker.ts | 0 .../tools/docgen/src/Configuration.ts | 0 .../tools/docgen/src/Core.ts | 0 .../tools/docgen/src/Domain.ts | 0 .../tools/docgen/src/Parser.ts | 0 .../tools/docgen/src/Printer.ts | 0 .../tools/docgen/src/bin.ts | 0 .../tools/docgen/src/index.ts | 0 .../docgen/src/internal/markdown-toc.d.ts | 0 .../tools/docgen/test/Checker.test.ts | 0 .../tools/docgen/test/Configuration.test.ts | 0 .../tools/docgen/test/Core.test.ts | 0 .../tools/docgen/test/Parser.test.ts | 0 .../docgen/test/fixtures/invalid-json.txt | 0 .../tools/docgen/tsconfig.json | 0 .../tools/doctest/CHANGELOG.md | 8 + .../tools/doctest/README.md | 0 .../tools/doctest/package.json | 10 +- .../tools/doctest/src/Plugin.ts | 0 .../tools/doctest/src/Protocol.ts | 0 .../tools/doctest/src/Runner.ts | 0 .../tools/doctest/src/Runtime.ts | 0 .../tools/doctest/src/Source.ts | 0 .../tools/doctest/src/Transform.ts | 0 .../tools/doctest/src/index.ts | 0 .../tools/doctest/test/Plugin.test.ts | 0 .../tools/doctest/test/Protocol.test.ts | 0 .../tools/doctest/test/Runtime.test.ts | 0 .../tools/doctest/test/Source.test.ts | 0 .../tools/doctest/test/Transform.test.ts | 0 .../test/fixtures/markdown/docs/helper.ts | 0 .../test/fixtures/markdown/docs/typed.md | 0 .../test/fixtures/markdown/docs/typed.mdx | 0 .../test/fixtures/markdown/src/typed.ts | 0 .../test/fixtures/markdown/vitest.config.ts | 0 .../tools/doctest/tsconfig.json | 0 .../tools/jsdocs/package.json | 4 +- .../tools/jsdocs/src/Jsdocs.ts | 90 +- .../tools/jsdocs/src/bin.ts | 0 .../tools/jsdocs/test/jsdocs.test.ts | 385 +- .../tools/jsdocs/tsconfig.json | 0 .../tools/openapi-generator/CHANGELOG.md | 8 + .../tools/openapi-generator/LICENSE | 0 .../tools/openapi-generator/README.md | 0 .../tools/openapi-generator/package.json | 4 +- .../src/HttpApiTransformer.ts | 0 .../src/JsonSchemaGenerator.ts | 0 .../openapi-generator/src/OpenApiGenerator.ts | 0 .../openapi-generator/src/OpenApiPatch.ts | 0 .../src/OpenApiTransformer.ts | 0 .../openapi-generator/src/ParsedOperation.ts | 0 .../tools/openapi-generator/src/Utils.ts | 0 .../tools/openapi-generator/src/bin.ts | 0 .../tools/openapi-generator/src/main.ts | 0 .../test/JsonSchemaGenerator.test.ts | 0 .../JsonSchemaGeneratorRepresentation.test.ts | 0 .../test/OpenApiGenerator.test.ts | 0 .../test/OpenApiGeneratorCli.test.ts | 0 .../test/OpenApiPatch.test.ts | 0 .../test/OpenApiTransformer.test.ts | 0 .../openapi-generator/test/Utils.test.ts | 0 .../test/fixtures/cli-basic-spec.json | 0 .../test/fixtures/cli-warning-spec.json | 0 .../test/fixtures/patches/invalid-op.json | 0 .../test/fixtures/patches/missing-path.json | 0 .../test/fixtures/patches/missing-value.json | 0 .../test/fixtures/patches/valid-add.json | 0 .../test/fixtures/patches/valid-multiple.json | 0 .../test/fixtures/patches/valid-patch.yaml | 0 .../test/fixtures/patches/valid-remove.json | 0 .../test/fixtures/patches/valid-replace.json | 0 .../tools/openapi-generator/tsconfig.json | 0 .../tools/oxc/oxlintrc.json | 0 .../tools/oxc/package.json | 6 +- .../tools/oxc/src/oxlint/index.ts | 0 .../src/oxlint/rules/no-bigint-literals.ts | 0 .../rules/no-import-from-barrel-package.ts | 0 .../oxlint/rules/no-js-extension-imports.ts | 0 .../oxlint/rules/no-opaque-instance-fields.ts | 0 .../src/oxlint/rules/no-unused-internal.ts | 0 .../no-import-from-barrel-package.test.ts | 0 .../oxc/test/no-js-extension-imports.test.ts | 0 .../test/no-opaque-instance-fields.test.ts | 0 .../tools/oxc/test/no-unused-internal.test.ts | 0 .../tools/oxc/test/utils.ts | 0 .../tools/oxc/tsconfig.json | 0 .../tools/utils/package.json | 2 +- .../tools/utils/src/Codegen.ts | 5 +- .../tools/utils/src/Glob.ts | 0 .../tools/utils/src/bin.ts | 0 .../tools/utils/src/commands/codegen.ts | 0 .../tools/utils/test/Codegen.test.ts | 17 + .../tools/utils/tsconfig.json | 0 .../packages => packages}/vitest/CHANGELOG.md | 8 + .../packages => packages}/vitest/LICENSE | 0 .../packages => packages}/vitest/README.md | 0 .../packages => packages}/vitest/package.json | 6 +- .../packages => packages}/vitest/src/index.ts | 65 +- .../vitest/src/internal/internal.ts | 0 .../packages => packages}/vitest/src/utils.ts | 22 + .../test/arbitrary-configuration.test.ts | 0 .../vitest/test/fixtures.test.ts | 0 .../vitest/test/index.test.ts | 0 .../vitest/test/isolation.test.ts | 0 .../vitest/test/layer-concurrency.test.ts | 0 .../vitest/test/nested-isolation.test.ts | 0 .../vitest/test/timeout.test.ts | 31 +- .../vitest/tsconfig.json | 0 .../vitest/typetest/index.tst.ts | 0 packages/vue-components/.prettierrc | 7 - packages/vue-components/.storybook/main.ts | 14 - packages/vue-components/.storybook/preview.ts | 52 - .../vue-components/.storybook/tsconfig.json | 9 - .../vue-components/.vscode/extensions.json | 7 - packages/vue-components/CHANGELOG.md | 6082 ----- packages/vue-components/README.md | 233 - .../__tests__/FixedNuxtErrorBoundary.test.ts | 213 - .../__tests__/IntegerValidation.test.ts | 49 - .../__tests__/OmegaForm.test.ts | 205 - .../OmegaForm/CustomFilterFieldSchema.test.ts | 37 - .../OmegaForm/DateValidation.test.ts | 68 - .../OmegaForm/DecimalSeparatorInput.test.ts | 115 - .../OmegaForm/DecodeTransformFallback.test.ts | 151 - .../OmegaForm/DefaultValuesPriority.test.ts | 23 - .../__tests__/OmegaForm/Defaults.test.ts | 551 - .../OmegaForm/Defaults.values.test.ts | 114 - .../OmegaForm/DialogBlockingI18n.test.ts | 82 - .../OmegaForm/EmailFormatDetection.test.ts | 11 - .../OmegaForm/EmptyStringValidation.test.ts | 77 - .../__tests__/OmegaForm/GeneralErrors.test.ts | 59 - .../OmegaForm/IndexedInputErrors.test.ts | 82 - .../OmegaForm/InputAttributes.test.ts | 221 - .../OmegaForm/InputReactivity.test.ts | 49 - .../__tests__/OmegaForm/InputRegistry.test.ts | 95 - .../OmegaForm/IntersectionError.test.ts | 223 - .../__tests__/OmegaForm/Meta.test.ts | 40 - .../NullableFieldInitialization.test.ts | 184 - .../NullableNestedStructValidation.test.ts | 148 - .../__tests__/OmegaForm/OmegaAutoGen.test.ts | 331 - .../__tests__/OmegaForm/OptionalKey.test.ts | 62 - .../__tests__/OmegaForm/RedactedMeta.test.ts | 181 - .../SelectMultipleLocalization.test.ts | 123 - .../__tests__/OmegaForm/SubmitEffect.test.ts | 142 - .../OmegaForm/SubmitErrorClear.test.ts | 59 - .../OmegaForm/TaggedUnionBehavior.test.ts | 251 - .../TaggedUnionLegacyWarning.test.ts | 31 - .../OmegaForm/TaggedUnionNested.test.ts | 54 - .../OmegaForm/TaggedUnionRequired.test.ts | 81 - .../OmegaForm/TaggedUnionRoot.test.ts | 77 - .../OmegaForm/TransformationSchema.test.ts | 84 - .../OmegaForm/TransformedStruct.test.ts | 174 - .../OmegaForm/UnregisteredFieldError.test.ts | 94 - .../WithDefaultConstructorPersistency.test.ts | 107 - .../createUseFormWithCustomInput.test.ts | 374 - .../__tests__/OmegaFormTest.vue | 62 - .../__tests__/OmegaIntlProvider.vue | 12 - .../migration/schema-v4-migration.test.ts | 703 - packages/vue-components/__tests__/setup.ts | 16 - .../__tests__/test-array-meta.test.ts | 55 - .../__tests__/test-nullor-duplicate.test.ts | 42 - .../__tests__/test-union-meta.test.ts | 17 - .../vue-components/__tests__/tsconfig.json | 9 - .../plans/2026-04-27-omegaform-refactor.md | 2655 -- .../2026-04-27-omegaform-refactor-design.md | 242 - packages/vue-components/env.d.ts | 8 - packages/vue-components/eslint.config.mjs | 20 - packages/vue-components/package.json | 95 - packages/vue-components/src/assets/.keep | 0 .../src/components/CommandButton.vue | 161 - .../vue-components/src/components/Dialog.vue | 58 - .../src/components/FixedNuxtErrorBoundary.vue | 112 - .../src/components/OmegaForm/InputProps.ts | 100 - .../src/components/OmegaForm/OmegaArray.vue | 76 - .../src/components/OmegaForm/OmegaAutoGen.vue | 105 - .../OmegaForm/OmegaErrorsInternal.vue | 238 - .../components/OmegaForm/OmegaFormInput.vue | 41 - .../src/components/OmegaForm/OmegaInput.vue | 95 - .../OmegaForm/OmegaInputVuetify.vue | 370 - .../OmegaForm/OmegaInternalInput.vue | 207 - .../components/OmegaForm/OmegaTaggedUnion.vue | 106 - .../OmegaForm/OmegaTaggedUnionInternal.vue | 41 - .../src/components/OmegaForm/OmegaWrapper.vue | 73 - .../src/components/OmegaForm/blockDialog.ts | 73 - .../OmegaForm/createUseFormWithCustomInput.ts | 70 - .../OmegaForm/decimalSeparatorInput.ts | 38 - .../src/components/OmegaForm/errors.ts | 139 - .../src/components/OmegaForm/getOmegaStore.ts | 31 - .../src/components/OmegaForm/hocs.ts | 19 - .../src/components/OmegaForm/index.ts | 27 - .../src/components/OmegaForm/inputs.ts | 22 - .../src/components/OmegaForm/meta/checks.ts | 107 - .../components/OmegaForm/meta/createMeta.ts | 140 - .../src/components/OmegaForm/meta/defaults.ts | 267 - .../src/components/OmegaForm/meta/redacted.ts | 71 - .../src/components/OmegaForm/meta/types.ts | 78 - .../src/components/OmegaForm/meta/walker.ts | 248 - .../OmegaForm/onMountedWithCleanup.ts | 22 - .../src/components/OmegaForm/persistency.ts | 247 - .../src/components/OmegaForm/submit.ts | 131 - .../src/components/OmegaForm/types.ts | 801 - .../src/components/OmegaForm/useOmegaForm.ts | 200 - .../components/OmegaForm/useRegisterField.ts | 17 - .../OmegaForm/validation/localized.ts | 242 - .../vue-components/src/components/index.ts | 4 - .../vue-components/src/components/style.css | 3 - packages/vue-components/src/index.ts | 20 - packages/vue-components/src/reset.css | 52 - packages/vue-components/src/utils/index.ts | 31 - .../stories/Commands.stories.ts | 46 - .../vue-components/stories/Commands/One.vue | 122 - .../stories/Commands/components.ts | 1 - .../stories/Commands/helpers.ts | 82 - .../stories/Commands/useEffectRouter.ts | 73 - .../vue-components/stories/Dialog.stories.ts | 34 - .../stories/Dialog/AsyncBlocking.vue | 47 - .../stories/Dialog/AsyncBlockingContent.vue | 91 - .../stories/Dialog/SimpleBlocking.vue | 49 - .../stories/Dialog/SimpleBlockingContent.vue | 30 - .../stories/Dialog/UploadBlocking.vue | 47 - .../stories/Dialog/UploadBlockingContent.vue | 105 - .../stories/FixedNuxtErrorBoundary.stories.ts | 18 - .../stories/FixedNuxtErrorBoundary/Demo.vue | 92 - .../stories/OmegaForm.stories.ts | 448 - .../stories/OmegaForm/Array.vue | 118 - .../stories/OmegaForm/AutoGeneration.vue | 74 - .../stories/OmegaForm/Booleans.vue | 42 - .../stories/OmegaForm/Clearable.vue | 25 - .../stories/OmegaForm/ComplexForm.vue | 147 - .../stories/OmegaForm/CustomInput.vue | 62 - .../OmegaForm/CustomInputClassName.vue | 53 - .../stories/OmegaForm/CustomLabelSlot.vue | 146 - .../vue-components/stories/OmegaForm/Date.vue | 35 - .../OmegaForm/DecodeTransformFallback.vue | 48 - .../stories/OmegaForm/Defaults.vue | 208 - .../OmegaForm/DialogBlockingExamples.vue | 76 - .../stories/OmegaForm/EmailForm.vue | 46 - .../stories/OmegaForm/EnterSubmitRepro.vue | 78 - .../stories/OmegaForm/FormTaggedUnion.vue | 116 - .../stories/OmegaForm/IndexedTable.vue | 79 - .../stories/OmegaForm/InputRegistry.vue | 109 - .../OmegaForm/IntegerValidationGerman.vue | 83 - .../stories/OmegaForm/IntersectionExample.vue | 55 - .../vue-components/stories/OmegaForm/Meta.vue | 92 - .../vue-components/stories/OmegaForm/Null.vue | 75 - .../stories/OmegaForm/Nullable.vue | 85 - .../OmegaForm/NullableNestedStruct.vue | 69 - .../stories/OmegaForm/NumberInput.vue | 68 - .../OmegaForm/OmitConstructorDefaults.vue | 131 - .../stories/OmegaForm/OptionalKey.vue | 142 - .../stories/OmegaForm/PersistencyForm.vue | 74 - ...rogrammaticallyHandleSubmitCheckErrors.vue | 39 - .../stories/OmegaForm/RatingInput.vue | 49 - .../stories/OmegaForm/Redacted.vue | 80 - .../OmegaForm/RootLevelTaggedUnion.vue | 127 - .../OmegaForm/RootLevelTaggedUnionLegacy.vue | 118 - .../stories/OmegaForm/SetErrorOnSubmit.vue | 35 - .../stories/OmegaForm/SimpleForm.vue | 43 - .../OmegaForm/SimpleFormVuetifyDefault.vue | 37 - .../stories/OmegaForm/SumExample.vue | 43 - .../stories/OmegaForm/Tanstack.vue | 210 - .../stories/OmegaForm/Union.vue | 77 - .../stories/OmegaForm/UsingOmegaForm.vue | 284 - .../OmegaForm/WindowExitPrevention.vue | 280 - .../OmegaForm/WithDefaultConstructor.vue | 53 - .../createUseFormWIthCustomInput.vue | 45 - .../stories/OmegaForm/form.Input.vue | 129 - packages/vue-components/stories/README.md | 29 - packages/vue-components/stories/tsconfig.json | 9 - packages/vue-components/tsconfig.build.json | 11 - packages/vue-components/tsconfig.json | 35 - .../vue-components/types/vuetify-shims.d.ts | 6 - packages/vue-components/vite.config.mts | 89 - packages/vue-components/vitest.config.mts | 19 - packages/vue/.madgerc | 7 - packages/vue/.npmignore | 14 - packages/vue/.prettierignore | 6 - packages/vue/CHANGELOG.md | 13734 ---------- packages/vue/docs/atom-query-api-redesign.md | 207 - packages/vue/docs/mutation-command-atoms.md | 40 - packages/vue/docs/query-key-invalidation.md | 107 - packages/vue/examples/streamMutation.ts | 72 - packages/vue/package.json | 102 - packages/vue/src/atomQuery.ts | 600 - packages/vue/src/commander.ts | 3408 --- packages/vue/src/confirm.ts | 34 - packages/vue/src/dependencyMetadata.ts | 84 - packages/vue/src/errorReporter.ts | 106 - packages/vue/src/form.ts | 501 - packages/vue/src/index.ts | 8 - packages/vue/src/internal/tanstackQuery.ts | 308 - packages/vue/src/intl.ts | 12 - packages/vue/src/lib.ts | 115 - packages/vue/src/liveQueryInvalidation.ts | 138 - packages/vue/src/makeClient.ts | 1314 - packages/vue/src/makeContext.ts | 34 - packages/vue/src/makeIntl.ts | 77 - packages/vue/src/makeUseCommand.ts | 29 - packages/vue/src/mutate.ts | 516 - packages/vue/src/query.ts | 940 - packages/vue/src/queryLifetime.ts | 74 - packages/vue/src/routeParams.ts | 55 - packages/vue/src/runtime.ts | 1 - packages/vue/src/suspense.ts | 43 - packages/vue/src/toast.ts | 1 - packages/vue/src/withToast.ts | 1 - packages/vue/test/Mutation.test.ts | 917 - .../vue/test/dependencyInvalidation.test.ts | 444 - .../vue/test/form-validation-errors.test.ts | 163 - packages/vue/test/form.test.ts | 280 - .../vue/test/inactiveInvalidation.test.ts | 172 - .../vue/test/interrupt-refetch-repro.test.ts | 625 - packages/vue/test/lib.test.ts | 240 - .../vue/test/liveQueryInvalidation.test.ts | 108 - packages/vue/test/makeClient.test.ts | 492 - packages/vue/test/query-span.test.ts | 92 - packages/vue/test/queryOptions.test.ts | 88 - packages/vue/test/streamFinal.test.ts | 64 - packages/vue/test/streamFn.test.ts | 457 - packages/vue/test/stubs.ts | 310 - packages/vue/test/suspense-regression.test.ts | 168 - packages/vue/test/suspense.test.ts | 160 - packages/vue/tsconfig.examples.json | 20 - packages/vue/tsconfig.json | 15 - packages/vue/tsconfig.src.json | 36 - packages/vue/tsconfig.test.json | 33 - packages/vue/vitest.config.ts | 18 - patches/.gitkeep | 0 patches/@ben_12__eslint-plugin-dprint.patch | 14 - patches/@changesets__cli@2.31.0.patch | 22 - .../@changesets__get-github-info@1.0.1.patch | 0 patches/@sendgrid__mail.patch | 23 - patches/@tanstack__query-core.patch | 203 - .../ts-plugin-sort-import-suggestions.patch | 45 - patches/typescript.patch | 159 - pnpm-lock.yaml | 21718 +++++++--------- pnpm-workspace.yaml | 70 +- repos/effect/.changeset/config.json | 55 - repos/effect/.envrc | 1 - repos/effect/.gitignore | 41 - repos/effect/.oxlintrc.json | 36 - repos/effect/.vscode/extensions.json | 6 - repos/effect/.vscode/settings.json | 51 - repos/effect/README.md | 97 - repos/effect/flake.lock | 27 - repos/effect/flake.nix | 24 - .../effect__ai-openai__OpenAiTelemetry.yaml | 9 - .../effect__sql-sqlite-bun__SqliteClient.yaml | 3 - ...effect__sql-sqlite-node__SqliteClient.yaml | 6 - repos/effect/package.json | 84 - .../packages/ai/cloudflare/CHANGELOG.md | 9 - .../effect/test/cluster/K8sHttpClient.test.ts | 34 - ...SqlEventLogServerUnencryptedStorageTest.ts | 169 - .../effect/test/httpapi/HttpApiError.test.ts | 29 - .../observability/OtlpSerialization.test.ts | 10 - .../effect/typetest/reactivity/Atom.tst.ts | 23 - ...ntLogServerUnencrypted.integration.test.ts | 7 - repos/effect/pnpm-lock.yaml | 12242 --------- repos/effect/pnpm-workspace.yaml | 36 - repos/effect/tsconfig.base.json | 45 - repos/effect/vitest.config.ts | 222 - repos/effect/vitest.setup.ts | 4 - .../effect/scratchpad => scratchpad}/index.ts | 0 .../scratchpad => scratchpad}/package.json | 0 .../scratchpad => scratchpad}/tsconfig.json | 0 scripts/agent-gh.mjs | 60 - scripts/agent-pr-ready.mjs | 65 - scripts/agent-pre-push.mjs | 140 - .../scripts => scripts}/bundle-analyze.sh | 0 .../bundle-compare-selected.sh | 0 .../scripts => scripts}/bundle-compare.sh | 0 .../scripts => scripts}/check-dist-types.mjs | 0 .../effect/scripts => scripts}/circular.mjs | 0 scripts/clean-dist.sh | 41 - {repos/effect/scripts => scripts}/clean.mjs | 0 {repos/effect/scripts => scripts}/codemod.mjs | 0 .../scripts => scripts}/codemods/jsdoc.ts | 0 scripts/convert_patch.js | 16 - .../scripts => scripts}/copy-ai-docs.mjs | 0 scripts/effect-bot.mjs | 116 + scripts/extract.sh | 26 - scripts/hooks/post-checkout.test.sh | 84 - scripts/install-git-hooks.mjs | 71 - scripts/lib/agent-env.mjs | 26 - scripts/lib/agent-gh-policy.mjs | 155 - scripts/lib/agent-pr-state.mjs | 119 - scripts/lib/agent-ship-gate-cache.mjs | 121 - scripts/mergeTsConfig.mjs | 49 - .../scripts => scripts}/package-scalar.mjs | 0 .../scripts => scripts}/package-swagger.mjs | 0 .../effect/scripts => scripts}/package.json | 0 scripts/rewrite-dist-declaration-imports.mjs | 37 - .../set-strip-internal.mjs | 0 .../scripts => scripts}/setup-agents.mjs | 0 scripts/sort.js | 31 - scripts/stagePublishPackage.mjs | 157 - scripts/sync-effect-subtree.js | 8 - .../effect/scripts => scripts}/tsconfig.json | 0 scripts/version.mjs | 8 + .../scripts => scripts}/worktree-setup.sh | 0 t3.json | 11 - tsconfig.all.json | 29 - tsconfig.base.json | 123 +- repos/effect/tsconfig.json => tsconfig.json | 0 tsconfig.lib.json | 14 - ...ig.packages.json => tsconfig.packages.json | 0 tsconfig.plugins.json | 27 - ...tsconfig.tests.json => tsconfig.tests.json | 3 +- repos/effect/tstyche.json => tstyche.json | 0 vite.config.base.ts | 40 - vitest.config.ts | 217 +- repos/effect/vitest.docs.ts => vitest.docs.ts | 0 vitest.setup.ts | 1 + wiki/Home.md | 3 - wiki/ResourceClients.md | 41 - wiki/bag-of-tricks.md | 95 - .../effect\342\200\220app\342\200\220cli.md" | 344 - wiki/error-reporter-migration.md | 174 - wiki/repository-derived-query-invalidation.md | 291 - 3670 files changed, 23819 insertions(+), 169931 deletions(-) rename {repos/effect/.agents => .agents}/AGENTS.md (100%) rename {repos/effect/.agents => .agents}/skills/ai-docs/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/bundle-analysis/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/changesets/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/changesets/authoring.md (97%) rename {repos/effect/.agents => .agents}/skills/ci-maintenance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/ci-maintenance/privileged-workflows.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/coordinated-upgrades.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/manifest-roles.md (100%) rename {repos/effect/.agents => .agents}/skills/effect-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/categories.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/declarations.md (86%) rename {repos/effect/.agents => .agents}/skills/jsdocs/examples.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/annotations.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/generation.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/dependencies.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/publishing.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/registration.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/runtime.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/types.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/displayed-types.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/runtime.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/types.md (100%) rename {repos/effect/.agents => .agents}/skills/vendored-assets/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/vendored-assets/browser-assets.md (100%) delete mode 100644 .changeset/README.md delete mode 100644 .changeset/active-query-invalidation.md delete mode 100644 .changeset/add-oxlint-integration.md delete mode 100644 .changeset/add-stream-mutation.md delete mode 100644 .changeset/adopt-module-system.md delete mode 100644 .changeset/align-invalidation-entry-types.md delete mode 100644 .changeset/align-schema-void-ts-semantics.md delete mode 100644 .changeset/all-phones-dream.md delete mode 100644 .changeset/amber-garlics-sing.md delete mode 100644 .changeset/api-client-isolate-layers.md delete mode 100644 .changeset/atom-query-vue.md delete mode 100644 .changeset/batch-cosmos-acks.md delete mode 100644 .changeset/batch-dual-options.md delete mode 100644 .changeset/big-teams-wash.md delete mode 100644 .changeset/blue-rats-admire.md delete mode 100644 .changeset/bold-berries-change.md delete mode 100644 .changeset/brave-animals-like.md delete mode 100644 .changeset/brave-bushes-fold.md delete mode 100644 .changeset/brave-laws-add.md delete mode 100644 .changeset/brave-oranges-try.md delete mode 100644 .changeset/brave-years-fry.md delete mode 100644 .changeset/bright-camels-grin.md delete mode 100644 .changeset/bright-lions-share.md delete mode 100644 .changeset/bright-mugs-paint.md delete mode 100644 .changeset/bright-peaches-wave.md delete mode 100644 .changeset/bright-trees-count.md delete mode 100644 .changeset/builtin-map-union-receiver.md delete mode 100644 .changeset/cached-per-request-helper.md delete mode 100644 .changeset/calm-clocks-observe.md delete mode 100644 .changeset/calm-cosmos-cluster.md delete mode 100644 .changeset/calm-filter-cycle.md delete mode 100644 .changeset/calm-pallets-listen.md delete mode 100644 .changeset/calm-pets-float.md delete mode 100644 .changeset/calm-picks-refresh.md delete mode 100644 .changeset/chilly-colts-attack.md delete mode 100644 .changeset/chilly-steaks-rule.md delete mode 100644 .changeset/chubby-dolls-yell.md delete mode 100644 .changeset/class-facade-encode-keys.md delete mode 100644 .changeset/clean-atom-suspense.md delete mode 100644 .changeset/clean-sql-data-column.md delete mode 100644 .changeset/clean-unused-dependencies.md delete mode 100644 .changeset/cleanup-tsgolint-codegen.md delete mode 100644 .changeset/clear-cycles-enjoy.md delete mode 100644 .changeset/clever-breads-enter.md delete mode 100644 .changeset/clever-pets-follow.md delete mode 100644 .changeset/cli-preserve-trailing-newline.md delete mode 100644 .changeset/cli-sync-no-tags.md delete mode 100644 .changeset/cli-ue-semver-target.md delete mode 100644 .changeset/client-fetch-property.md delete mode 100644 .changeset/cluster-servicebus-notifications.md delete mode 100644 .changeset/codegen-rule-options.md delete mode 100644 .changeset/cold-brooms-ski.md delete mode 100644 .changeset/cold-rabbits-pay.md delete mode 100644 .changeset/command-button-optional-input.md delete mode 100644 .changeset/commander-state-toast-types.md delete mode 100644 .changeset/commander-stream-fn.md delete mode 100644 .changeset/common-schools-stop.md delete mode 100644 .changeset/computed-projections-extensions.md delete mode 100644 .changeset/computed-projections-infra.md delete mode 100644 .changeset/concurrent-decode-helpers.md delete mode 100644 .changeset/config-nested-native.md delete mode 100644 .changeset/configurable-barrel-import-extension.md delete mode 100644 .changeset/configurable-mutation-invalidation.md delete mode 100644 .changeset/context-map-resolver-cache.md delete mode 100644 .changeset/cool-needles-wash.md delete mode 100644 .changeset/core-boundary-split.md delete mode 100644 .changeset/cosmos-autoscale-containers.md delete mode 100644 .changeset/cosmos-cluster-occ-tests.md delete mode 100644 .changeset/cosmos-every-param-shift.md delete mode 100644 .changeset/cosmos-filter-seed.md delete mode 100644 .changeset/cosmos-namespace-spans.md delete mode 100644 .changeset/cosmos-namespace-support.md delete mode 100644 .changeset/cosmos-span-response-size.md delete mode 100644 .changeset/cosmos-workflow-engine.md delete mode 100644 .changeset/cozy-cats-lie.md delete mode 100644 .changeset/crisp-seals-care.md delete mode 100644 .changeset/crisp-trees-smash.md delete mode 100644 .changeset/ctxmap-per-request-memomap.md delete mode 100644 .changeset/cuddly-laws-smell.md delete mode 100644 .changeset/cups-error-tagged.md delete mode 100644 .changeset/curly-beds-sing.md delete mode 100644 .changeset/curly-dolls-double.md delete mode 100644 .changeset/curvy-files-share.md delete mode 100644 .changeset/curvy-pugs-rush.md delete mode 100644 .changeset/cyan-experts-smoke.md delete mode 100644 .changeset/cyan-pianos-peel.md delete mode 100644 .changeset/cyan-things-train.md delete mode 100644 .changeset/date-defaults-pipe-self.md delete mode 100644 .changeset/date-identifier-split.md delete mode 100644 .changeset/delay-waiting-toast.md delete mode 100644 .changeset/disable-query-invalidation.md delete mode 100644 .changeset/document-query-project-modes.md delete mode 100644 .changeset/drop-constructor-default.md delete mode 100644 .changeset/easy-aliens-peel.md delete mode 100644 .changeset/easy-spiders-study.md delete mode 100644 .changeset/effa-sync-shared.md delete mode 100644 .changeset/effa-sync-update-flag.md delete mode 100644 .changeset/effect-app-arbitrary-email-phone.md delete mode 100644 .changeset/effect-app-prefer-fn-untraced.md delete mode 100644 .changeset/effect-app-realistic-number-samples.md delete mode 100644 .changeset/effect-main-rc-apis.md delete mode 100644 .changeset/eighty-aliens-hide.md delete mode 100644 .changeset/eighty-signs-smile.md delete mode 100644 .changeset/eleven-eyes-lie.md delete mode 100644 .changeset/empty-queens-begin.md delete mode 100644 .changeset/empty-rules-check.md delete mode 100644 .changeset/eslint-codegen-glob-11.md delete mode 100644 .changeset/expose-make-on-query-command.md delete mode 100644 .changeset/extract-fixed-nuxt-error-boundary.md delete mode 100644 .changeset/facade-hardening-error-facade.md delete mode 100644 .changeset/fair-baboons-marry.md delete mode 100644 .changeset/fair-oranges-wave.md delete mode 100644 .changeset/fair-tables-march.md delete mode 100644 .changeset/famous-plums-watch.md delete mode 100644 .changeset/fancy-ducks-run.md delete mode 100644 .changeset/fast-loops-scream.md delete mode 100644 .changeset/fiery-feet-sleep.md delete mode 100644 .changeset/fiery-suits-kneel.md delete mode 100644 .changeset/five-trams-repeat.md delete mode 100644 .changeset/fix-class-type-export.md delete mode 100644 .changeset/fix-cli-gist-cache-id.md delete mode 100644 .changeset/fix-client-input-stream.md delete mode 100644 .changeset/fix-contextmap-leak.md delete mode 100644 .changeset/fix-cosmos-cluster-deferred.md delete mode 100644 .changeset/fix-cosmos-cluster-machineid.md delete mode 100644 .changeset/fix-cosmos-serverless-create.md delete mode 100644 .changeset/fix-cosmos-workflow-engine.md delete mode 100644 .changeset/fix-is-generator-function.md delete mode 100644 .changeset/fix-json-schema-annotations.md delete mode 100644 .changeset/fix-jsonschema-postprocess.md delete mode 100644 .changeset/fix-onedsl-r-inference.md delete mode 100644 .changeset/fix-pascalcase-barrel.md delete mode 100644 .changeset/fix-path-recursive-index-signature.md delete mode 100644 .changeset/fix-project-encodekeys-selection.md delete mode 100644 .changeset/fix-projection-union-adapter.md delete mode 100644 .changeset/fix-query-defaultvalues-double-encode.md delete mode 100644 .changeset/fix-repo-transformed-id-union.md delete mode 100644 .changeset/fix-repo-transformed-id.md delete mode 100644 .changeset/fix-resolver-cache-precheck.md delete mode 100644 .changeset/fix-schema-interface-widening.md delete mode 100644 .changeset/fix-seed-marker-and-circular-dep.md delete mode 100644 .changeset/fix-sql-array-relation-queries.md delete mode 100644 .changeset/fix-sql-in-null-handling.md delete mode 100644 .changeset/fix-sql-includes-serialize.md delete mode 100644 .changeset/fix-sql-json-from-string-codec.md delete mode 100644 .changeset/fix-sql-namespace-params.md delete mode 100644 .changeset/fix-sql-pg-seed-migrations-table.md delete mode 100644 .changeset/fix-sql-select-etag-column.md delete mode 100644 .changeset/fix-sql-select-types-and-defaults.md delete mode 100644 .changeset/fix-sql-whereevery-flip.md delete mode 100644 .changeset/fix-sqlite-per-ns-seed-migrations-table.md delete mode 100644 .changeset/fix-transformed-struct-defaults.md delete mode 100644 .changeset/fix-vue-form-numeric-type-detection.md delete mode 100644 .changeset/fix-vue-interrupt-tests.md delete mode 100644 .changeset/fix-vue-lint-typescript-version.md delete mode 100644 .changeset/flat-turkeys-invent.md delete mode 100644 .changeset/flatten-nested-anyof.md delete mode 100644 .changeset/forty-bottles-tease.md delete mode 100644 .changeset/frank-ends-switch.md delete mode 100644 .changeset/frank-memes-sin.md delete mode 100644 .changeset/frank-papayas-design.md delete mode 100644 .changeset/fresh-buckets-pump.md delete mode 100644 .changeset/fresh-candles-shake.md delete mode 100644 .changeset/fresh-indexed-fields.md delete mode 100644 .changeset/fresh-mails-visit.md delete mode 100644 .changeset/fresh-paths-say.md delete mode 100644 .changeset/fresh-pianos-argue.md delete mode 100644 .changeset/friendly-points-drum.md delete mode 100644 .changeset/fruity-meteors-fix.md delete mode 100644 .changeset/full-oranges-heal.md delete mode 100644 .changeset/full-pugs-fall.md delete mode 100644 .changeset/funky-guests-switch.md delete mode 100644 .changeset/funny-webs-clap.md delete mode 100644 .changeset/fuzzy-eels-decide.md delete mode 100644 .changeset/fuzzy-eggs-jump.md delete mode 100644 .changeset/gentle-bats-leave.md delete mode 100644 .changeset/gentle-donkeys-tease.md delete mode 100644 .changeset/gold-ghosts-laugh.md delete mode 100644 .changeset/gold-wolves-shake.md delete mode 100644 .changeset/green-coins-talk.md delete mode 100644 .changeset/green-frogs-check.md delete mode 100644 .changeset/green-snakes-poke.md delete mode 100644 .changeset/green-taxis-project-hash.md delete mode 100644 .changeset/green-taxis-rule.md delete mode 100644 .changeset/handle-mutate-request-always-functions.md delete mode 100644 .changeset/handler-input-make-aligned.md delete mode 100644 .changeset/heavy-bears-appear.md delete mode 100644 .changeset/heavy-ducks-show.md delete mode 100644 .changeset/honest-planets-fry.md delete mode 100644 .changeset/http-span-name.md delete mode 100644 .changeset/huge-carrots-deny.md delete mode 100644 .changeset/humble-walls-fail.md delete mode 100644 .changeset/icy-paws-return.md delete mode 100644 .changeset/indefinite-waiting-toast.md delete mode 100644 .changeset/infra-generate-from-schema-seed.md delete mode 100644 .changeset/infra-prefer-fn-untraced.md delete mode 100644 .changeset/infra-sentry-node-dep.md delete mode 100644 .changeset/invalidation-set-add-shortcut-and-batch.md delete mode 100644 .changeset/itchy-mangos-find.md delete mode 100644 .changeset/jitm-json-store-boundary.md delete mode 100644 .changeset/khaki-frogs-wave.md delete mode 100644 .changeset/kind-lizards-bathe.md delete mode 100644 .changeset/large-pugs-strive.md delete mode 100644 .changeset/late-forks-obey.md delete mode 100644 .changeset/lazy-boats-lay.md delete mode 100644 .changeset/lazy-model-facades.md delete mode 100644 .changeset/legal-weeks-raise.md delete mode 100644 .changeset/lemon-emus-behave.md delete mode 100644 .changeset/little-berries-sip.md delete mode 100644 .changeset/little-planets-bake.md delete mode 100644 .changeset/live-query-dependencies.md delete mode 100644 .changeset/long-regions-change.md delete mode 100644 .changeset/loud-planets-cheer.md delete mode 100644 .changeset/lucky-papayas-fetch.md delete mode 100644 .changeset/lucky-streets-add.md delete mode 100644 .changeset/lucky-zebras-invent.md delete mode 100644 .changeset/major-drinks-stop.md delete mode 100644 .changeset/major-planets-lie.md delete mode 100644 .changeset/many-ideas-report.md delete mode 100644 .changeset/meta-codegen-no-export.md delete mode 100644 .changeset/meta-module-name-override.md delete mode 100644 .changeset/metal-mammals-smell.md delete mode 100644 .changeset/middleware-error-from-dynamic-rcm.md delete mode 100644 .changeset/middleware-errors-via-rpc-tag.md delete mode 100644 .changeset/mighty-hoops-help.md delete mode 100644 .changeset/migrate-tsgolint.md delete mode 100644 .changeset/modern-humans-chew.md delete mode 100644 .changeset/moody-peas-smoke.md delete mode 100644 .changeset/mutate-invalidation-shorthands.md delete mode 100644 .changeset/mutate-wrap-stream-no-args.md delete mode 100644 .changeset/namespaced-change-feed.md delete mode 100644 .changeset/native-encoded-query-json.md delete mode 100644 .changeset/neat-brooms-wave.md delete mode 100644 .changeset/neat-dingos-tease.md delete mode 100644 .changeset/neat-pumpkins-juggle.md delete mode 100644 .changeset/new-badgers-wait.md delete mode 100644 .changeset/new-berries-march.md delete mode 100644 .changeset/nice-jars-grow.md delete mode 100644 .changeset/nice-needles-film.md delete mode 100644 .changeset/nice-shrimps-fold.md delete mode 100644 .changeset/ninety-stamps-end.md delete mode 100644 .changeset/no-await-effect.md delete mode 100644 .changeset/number-input-dual-separator.md delete mode 100644 .changeset/odd-deers-work.md delete mode 100644 .changeset/odd-tigers-scope.md delete mode 100644 .changeset/old-snakes-film.md delete mode 100644 .changeset/old-suns-kneel.md delete mode 100644 .changeset/olive-onions-lose.md delete mode 100644 .changeset/omega-native-input-types.md delete mode 100644 .changeset/omega-nullable-nested-struct.md delete mode 100644 .changeset/omegaform-between-localization.md delete mode 100644 .changeset/omegaform-decodeto-meta-from-encoded.md delete mode 100644 .changeset/omegaform-enter-submit-repro.md delete mode 100644 .changeset/omegaform-input-registry.md delete mode 100644 .changeset/omegaform-module-split.md delete mode 100644 .changeset/omegaform-typecheck-stories.md delete mode 100644 .changeset/omegaform-vnumber-input.md delete mode 100644 .changeset/optimize-codegen-barrel-model.md delete mode 100644 .changeset/otel-semconv-spans.md delete mode 100644 .changeset/payload-summary-recurse.md delete mode 100644 .changeset/per-namespace-seeding.md delete mode 100644 .changeset/petite-tables-cover.md delete mode 100644 .changeset/polite-chefs-drum.md delete mode 100644 .changeset/pre.json delete mode 100644 .changeset/project-computed-type-safety.md delete mode 100644 .changeset/project-encoded-type-constraint.md delete mode 100644 .changeset/project-union-schema.md delete mode 100644 .changeset/project-union-subsets.md delete mode 100644 .changeset/projectable-from-domain.md delete mode 100644 .changeset/projectable-key-guard-project.md delete mode 100644 .changeset/provide-dual.md delete mode 100644 .changeset/publish-tsconfig-postpack.md delete mode 100644 .changeset/pure-usesync-cleanup.md delete mode 100644 .changeset/query-aggregate-support.md delete mode 100644 .changeset/query-map-has-ops.md delete mode 100644 .changeset/query-optional-mode.md delete mode 100644 .changeset/query-span-propagation.md delete mode 100644 .changeset/queryraw-stored-json.md delete mode 100644 .changeset/quick-seals-relate.md delete mode 100644 .changeset/quick-wolves-yawn.md delete mode 100644 .changeset/quiet-shrimps-juggle.md delete mode 100644 .changeset/quiet-tigers-reply.md delete mode 100644 .changeset/quiet-tools-rest.md delete mode 100644 .changeset/rare-llamas-begin.md delete mode 100644 .changeset/rare-rules-help.md delete mode 100644 .changeset/ready-bags-wave.md delete mode 100644 .changeset/ready-bananas-scream.md delete mode 100644 .changeset/real-lilies-design.md delete mode 100644 .changeset/real-suits-clean.md delete mode 100644 .changeset/recover-stuck-query-interrupts.md delete mode 100644 .changeset/red-pillows-repeat.md delete mode 100644 .changeset/relation-collect-fields.md delete mode 100644 .changeset/remove-by-id-array.md delete mode 100644 .changeset/remove-class-struct-pick-omit.md delete mode 100644 .changeset/remove-legacy-from-make-client.md delete mode 100644 .changeset/remove-legacy-mutate-result-2.md delete mode 100644 .changeset/remove-lodash-from-effect-app.md delete mode 100644 .changeset/remove-operations.md delete mode 100644 .changeset/remove-queue-machinery.md delete mode 100644 .changeset/remove-tagged-error-compat.md delete mode 100644 .changeset/remove-tagged-request.md delete mode 100644 .changeset/remove-void-schema-override.md delete mode 100644 .changeset/remove-withDefaultConstructor.md delete mode 100644 .changeset/rename-mutate-stream.md delete mode 100644 .changeset/rename-withdefault-to-withconstructordefault.md delete mode 100644 .changeset/replace-proxify-with-accessors.md delete mode 100644 .changeset/repo-derived-query-invalidation.md delete mode 100644 .changeset/repo-mutations-accept-empty-arrays.md delete mode 100644 .changeset/repo-span-topology.md delete mode 100644 .changeset/repo-spans-itemtype.md delete mode 100644 .changeset/repository-registry.md delete mode 100644 .changeset/request-scoped-dependencies.md delete mode 100644 .changeset/require-middleware-layers.md delete mode 100644 .changeset/resolver-cache-use-official.md delete mode 100644 .changeset/restore-tight-project-guard.md delete mode 100644 .changeset/router-no-meta.md delete mode 100644 .changeset/rpc-stream-support.md delete mode 100644 .changeset/salty-weeks-walk.md delete mode 100644 .changeset/schema-concurrency-unbounded.md delete mode 100644 .changeset/schema-encode-slow-metrics.md delete mode 100644 .changeset/schema-facade-identifier-structfacade.md delete mode 100644 .changeset/schema-json-collections.md delete mode 100644 .changeset/secret-url-tests.md delete mode 100644 .changeset/select-delayed-cache-invalidation.md delete mode 100644 .changeset/sendgrid-configurable-fake-address.md delete mode 100644 .changeset/server-driven-cache-invalidation.md delete mode 100644 .changeset/server-invalidation-handler-shorthand.md delete mode 100644 .changeset/servicebus-log-only-spans.md delete mode 100644 .changeset/shaggy-terms-show.md delete mode 100644 .changeset/shaggy-waves-slide.md delete mode 100644 .changeset/shaggy-wolves-grow.md delete mode 100644 .changeset/share-oxlintrc-config.md delete mode 100644 .changeset/sharp-buses-bow.md delete mode 100644 .changeset/sharp-seals-clean.md delete mode 100644 .changeset/short-cats-march.md delete mode 100644 .changeset/short-frogs-add.md delete mode 100644 .changeset/short-suns-travel.md delete mode 100644 .changeset/shy-squids-make.md delete mode 100644 .changeset/silent-results-shave.md delete mode 100644 .changeset/silent-rooms-throw.md delete mode 100644 .changeset/silly-eggs-roll.md delete mode 100644 .changeset/single-invalidate-query-call.md delete mode 100644 .changeset/six-regions-start.md delete mode 100644 .changeset/six-yaks-stop.md delete mode 100644 .changeset/sixty-meals-sin.md delete mode 100644 .changeset/slimy-apes-film.md delete mode 100644 .changeset/slimy-lions-laugh.md delete mode 100644 .changeset/slimy-schools-fold.md delete mode 100644 .changeset/slow-readers-wave.md delete mode 100644 .changeset/small-papers-sneeze.md delete mode 100644 .changeset/small-wolves-knock.md delete mode 100644 .changeset/smart-beers-listen.md delete mode 100644 .changeset/smart-candles-rest.md delete mode 100644 .changeset/soft-books-win.md delete mode 100644 .changeset/soft-guests-relax.md delete mode 100644 .changeset/soft-mails-brush.md delete mode 100644 .changeset/soft-work-invalidations.md delete mode 100644 .changeset/sour-turtles-smash.md delete mode 100644 .changeset/source-first-package-exports.md delete mode 100644 .changeset/sparkly-cars-press.md delete mode 100644 .changeset/spicy-falcons-rush.md delete mode 100644 .changeset/spicy-lights-trade.md delete mode 100644 .changeset/sql-namespace-support.md delete mode 100644 .changeset/sql-store-adapter.md delete mode 100644 .changeset/sql-transaction-middleware.md delete mode 100644 .changeset/sqlite-per-namespace-layermap.md delete mode 100644 .changeset/sqlite-workflow-engine.md delete mode 100644 .changeset/sse-context-map-scope.md delete mode 100644 .changeset/sse-filter-namespace-by-storeid.md delete mode 100644 .changeset/staged-publish-directory.md delete mode 100644 .changeset/stale-actors-remain.md delete mode 100644 .changeset/stale-emus-see.md delete mode 100644 .changeset/stale-papers-join.md delete mode 100644 .changeset/store-decode-schema-error.md delete mode 100644 .changeset/store-effect-cached.md delete mode 100644 .changeset/store-json-values.md delete mode 100644 .changeset/store-seed-uninterruptible.md delete mode 100644 .changeset/stream-callable-progress.md delete mode 100644 .changeset/stream-drain-data-dependencies.md delete mode 100644 .changeset/stream-effect-fail.md delete mode 100644 .changeset/stream-final-default-success.md delete mode 100644 .changeset/stream-final-type.md delete mode 100644 .changeset/stream-first-write-flush.md delete mode 100644 .changeset/stream-fn-enhancements.md delete mode 100644 .changeset/stream-metadata-v1.md delete mode 100644 .changeset/stream-on-command-query.md delete mode 100644 .changeset/stream-withdefaulttoast.md delete mode 100644 .changeset/strict-stream-handlers.md delete mode 100644 .changeset/stringid-native-arbitrary.md delete mode 100644 .changeset/strong-dots-sin.md delete mode 100644 .changeset/struct-pick-omit.md delete mode 100644 .changeset/structfacade-effect-app-struct.md delete mode 100644 .changeset/suspense-keep-previous-value.md delete mode 100644 .changeset/sweet-humans-ring.md delete mode 100644 .changeset/sweet-moments-see.md delete mode 100644 .changeset/swift-owls-knit.md delete mode 100644 .changeset/swift-tables-turn.md delete mode 100644 .changeset/sync-effect-app-command.md delete mode 100644 .changeset/sync-effect-subtree.md delete mode 100644 .changeset/sync-effect-url.md delete mode 100644 .changeset/sync-push-deletions.md delete mode 100644 .changeset/synchronous-change-feed.md delete mode 100644 .changeset/tagged-request-command-query-split.md delete mode 100644 .changeset/tagged-request-for.md delete mode 100644 .changeset/tagged-union-drop-fields-constraint.md delete mode 100644 .changeset/tagged-union-generate-guards.md delete mode 100644 .changeset/tagged-union-generate-guards2.md delete mode 100644 .changeset/tall-boxes-relax.md delete mode 100644 .changeset/tall-hats-reflect.md delete mode 100644 .changeset/tanstack-effect-equal-structural-sharing.md delete mode 100644 .changeset/tanstack-suspense-seed.md delete mode 100644 .changeset/tasty-eggs-stare.md delete mode 100644 .changeset/tasty-fans-accept.md delete mode 100644 .changeset/ten-crews-like.md delete mode 100644 .changeset/ten-suns-shine.md delete mode 100644 .changeset/tender-bars-ring.md delete mode 100644 .changeset/tender-donkeys-pick.md delete mode 100644 .changeset/thin-zebras-press.md delete mode 100644 .changeset/thirty-cooks-pump.md delete mode 100644 .changeset/thirty-experts-taste.md delete mode 100644 .changeset/three-hotels-roll.md delete mode 100644 .changeset/tidy-caches-reflect.md delete mode 100644 .changeset/tidy-cosmos-batches.md delete mode 100644 .changeset/tidy-derived-dependencies.md delete mode 100644 .changeset/tidy-dprint-oxlint-config.md delete mode 100644 .changeset/tidy-effects-diagnose.md delete mode 100644 .changeset/tidy-forms-report.md delete mode 100644 .changeset/tidy-pans-rule.md delete mode 100644 .changeset/tidy-poets-think.md delete mode 100644 .changeset/tidy-remaining-formatting.md delete mode 100644 .changeset/tidy-rivers-identify.md delete mode 100644 .changeset/tidy-titles-remove.md delete mode 100644 .changeset/tiny-cows-repair.md delete mode 100644 .changeset/tiny-gorillas-jump.md delete mode 100644 .changeset/tired-insects-stand.md delete mode 100644 .changeset/toast-group-request-id.md delete mode 100644 .changeset/tough-apes-whisper.md delete mode 100644 .changeset/tsgo-decodingservices-workaround.md delete mode 100644 .changeset/tsgo-direct-schema-property-access.md delete mode 100644 .changeset/twenty-seas-grab.md delete mode 100644 .changeset/two-clocks-wash.md delete mode 100644 .changeset/typed-database-error.md delete mode 100644 .changeset/typed-query-builders.md delete mode 100644 .changeset/unify-emailer-spans.md delete mode 100644 .changeset/unify-parsemany-spans.md delete mode 100644 .changeset/update-effect-4.0.0.md delete mode 100644 .changeset/update-effect-4.0.1.md delete mode 100644 .changeset/update-effect-beta-107.md delete mode 100644 .changeset/update-effect-beta-36.md delete mode 100644 .changeset/update-effect-beta-52.md delete mode 100644 .changeset/update-effect-beta-60-rpc-custom.md delete mode 100644 .changeset/update-effect-beta-66.md delete mode 100644 .changeset/update-effect-beta-67.md delete mode 100644 .changeset/update-effect-beta-83.md delete mode 100644 .changeset/update-effect-beta-84.md delete mode 100644 .changeset/update-effect-beta-86.md delete mode 100644 .changeset/update-effect-beta-88.md delete mode 100644 .changeset/update-effect-beta-90.md delete mode 100644 .changeset/update-effect-pkgprnew.md delete mode 100644 .changeset/update-effect-rc-112.md delete mode 100644 .changeset/update-effect-rc-115.md delete mode 100644 .changeset/upset-aliens-bathe.md delete mode 100644 .changeset/upset-bugs-roll.md delete mode 100644 .changeset/version-independent-snapshot.md delete mode 100644 .changeset/vue-components-src-exports.md delete mode 100644 .changeset/vue-components-vuetify-typecheck.md delete mode 100644 .changeset/vue-placeholder-data-fallback.md delete mode 100644 .changeset/vue-prefer-fn-untraced.md delete mode 100644 .changeset/vue-stream-query.md delete mode 100644 .changeset/vue-suspense-result-race.md delete mode 100644 .changeset/vue-unified-invalidation-foreach.md delete mode 100644 .changeset/wacky-deer-decide.md delete mode 100644 .changeset/warm-maps-reflect.md delete mode 100644 .changeset/warm-streets-smell.md delete mode 100644 .changeset/wet-roses-press.md delete mode 100644 .changeset/wet-sites-fall.md delete mode 100644 .changeset/wild-olives-bathe.md delete mode 100644 .changeset/wild-wombats-nail.md delete mode 100644 .changeset/wise-items-warn.md delete mode 100644 .changeset/wise-stamps-crash.md delete mode 100644 .changeset/withdefaulttoaststream-progress.md delete mode 100644 .changeset/witty-bats-return.md delete mode 100644 .changeset/wrap-stream-command.md delete mode 100644 .claude/settings.json delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .devcontainer/humanlog.sh delete mode 100644 .devcontainer/usepnpm.sh delete mode 100644 .githooks/README.md delete mode 100755 .githooks/post-checkout delete mode 100755 .githooks/pre-commit delete mode 100755 .githooks/pre-push delete mode 100644 .github/FUNDING.yml rename {repos/effect/.github => .github}/actions/deploy-website/action.yml (100%) rename {repos/effect/.github => .github}/actions/setup/action.yaml (100%) delete mode 100644 .github/npm-trusted-publishing.md rename {repos/effect/.github => .github}/workflows/ai-codegen.yml (100%) rename {repos/effect/.github => .github}/workflows/bundle-comment.yml (100%) rename {repos/effect/.github => .github}/workflows/check.yml (98%) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/claude.yml rename {repos/effect/.github => .github}/workflows/cluster.yml (100%) create mode 100644 .github/workflows/effect-bot.yml delete mode 100644 .github/workflows/publish-snapshot.yml delete mode 100644 .github/workflows/publish-wiki.yml rename {repos/effect/.github => .github}/workflows/release-queue.yml (100%) delete mode 100644 .github/workflows/release.yaml rename {repos/effect/.github => .github}/workflows/release.yml (100%) rename {repos/effect/.github => .github}/workflows/snapshot.yml (100%) delete mode 100644 .ncurc.json delete mode 100644 .npmrc delete mode 100644 .nvmrc delete mode 100644 .specs/Update-to-latest-Effect.md delete mode 100644 .specs/changeset-publishing.md delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/model.code-snippets delete mode 100644 .vscode/service.code-snippets delete mode 100644 .vscode/tasks.json delete mode 100644 AGENTS.md delete mode 100644 CLAUDE.md rename repos/effect/LICENSE => LICENSE (100%) rename repos/effect/LLMS.md => LLMS.md (100%) rename repos/effect/MIGRATION.md => MIGRATION.md (94%) rename {repos/effect/ai-docs => ai-docs}/README.md (100%) rename {repos/effect/ai-docs => ai-docs}/package.json (95%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/01_effect-gen.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/02_effect-fn.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/10_creating-effects.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/02_schema/10_schema-basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/02_schema/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/01_service.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/10_reference.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/20_layer-composition.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/20_layer-unwrap.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/01_error-handling.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/10_catch-tags.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/20_reason-errors.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/10_acquire-release.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/20_layer-side-effects.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/30_layer-map.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/10_run-main.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/20_layer-launch.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/07_pubsub/10_pubsub.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/07_pubsub/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/10_creating-streams.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/20_consuming-streams.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/30_encoding.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/04_integration/10_managed-runtime.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/04_integration/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/05_batching/10_request-resolver.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/05_batching/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/06_schedule/10_schedules.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/06_schedule/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/10_creating-and-formatting.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/20_time-zones.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/10_logging.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/20_otlp-tracing.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/10_effect-tests.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/20_layer-tests.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/10_predicate/01_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/10_predicate/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/40_sql/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/40_sql/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/50_http-client/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/50_http-client/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/20_testing.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Api.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Authorization.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/System.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Users.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/domain/User.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/domain/UserErrors.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Authorization.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Users.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Users/http.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/60_child-process/10_working-with-child-processes.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/60_child-process/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/70_cli/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/70_cli/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/10_language-model.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/20_tools.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/30_chat.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/fixtures/domain/LaunchPlan.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/80_cluster/10_entities.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/80_cluster/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/tsconfig.json (87%) delete mode 100644 assets/dev/effect-app-apple-touch-180.png rename repos/effect/deno.json => deno.json (100%) rename repos/effect/dprint.json => dprint.json (100%) delete mode 100644 dprint.jsonc rename repos/effect/jsdocs.config.json => jsdocs.config.json (92%) delete mode 100644 lint-staged.config.js rename {repos/effect/migration => migration}/annotations/README.md (100%) rename {repos/effect/migration => migration}/annotations/effect__Arbitrary.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Array.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__BigDecimal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__BigInt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Brand.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cause.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Channel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ChildExecutorDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Chunk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Clock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Config.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigProvider.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigProviderPathPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Console.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Context.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cron.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Data.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__DateTime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__DefaultServices.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Deferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Differ.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Duration.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Effect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Effectable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Either.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Encoding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Equal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Equivalence.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ExecutionPlan.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ExecutionStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Exit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FastCheck.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Fiber.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberHandle.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRefs.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRefsPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberStatus.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Function.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__GlobalValue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Graph.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__GroupBy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Hash.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__HashMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__HashSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Inspectable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Iterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__JSONSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__KeyedPool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Layer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LayerMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__List.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LogLevel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LogSpan.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Logger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Mailbox.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ManagedRuntime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Match.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeState.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Metric.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricBoundaries.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricHook.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricKey.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricKeyType.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricLabel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricPair.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricPolling.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricRegistry.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricState.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Micro.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ModuleVersion.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableHashMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableHashSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Number.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Option.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Order.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Ordering.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ParseResult.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__PartitionedSemaphore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pipeable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Predicate.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pretty.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__PubSub.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Queue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Random.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RcMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RcRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Readable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Record.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RedBlackTree.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Redacted.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Ref.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Reloadable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Request.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RequestBlock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RequestResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Resource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Runtime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RuntimeFlags.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RuntimeFlagsPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__STM.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Schedule.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleInterval.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleIntervals.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Scheduler.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Schema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SchemaAST.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Scope.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScopedCache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScopedRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Secret.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SingleProducerAsyncInput.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Sink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SortedMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SortedSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Stream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__StreamEmit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__StreamHaltStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Streamable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Struct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Subscribable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SubscriptionRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Supervisor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Symbol.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SynchronizedRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TArray.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TDeferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TPriorityQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TPubSub.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TRandom.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TReentrantLock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSemaphore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSubscriptionRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Take.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotation.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotationMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotations.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestClock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestLive.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestServices.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestSized.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Tracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Trie.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Tuple.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Types.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__UpstreamPullRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__UpstreamPullStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Utils.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiLanguageModel.yaml (100%) create mode 100644 migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiTokenizer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__AiError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Chat.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__EmbeddingModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__IdGenerator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__LanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__McpSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__McpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Model.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Prompt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Response.yaml (100%) create mode 100644 migration/annotations/effect__ai__Telemetry.yaml rename {repos/effect/migration => migration}/annotations/effect__ai__Tool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Toolkit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Args.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__AutoCorrect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__BuiltInOptions.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CliApp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CliConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Command.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CommandDescriptor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CommandDirective.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__ConfigFile.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__HelpDoc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__HelpDoc__Span.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Options.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Primitive.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Prompt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Usage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__ValidationError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterCron.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterWorkflowEngine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__DeliverAt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Entity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityProxy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityProxyServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityResource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Envelope.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__HttpRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__K8sHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__MachineId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Message.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__MessageStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Reply.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Runner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerHealth.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Runners.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Sharding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardingConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardingRegistrationEvent.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SingleRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Singleton.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SingletonAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Snowflake.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SocketRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SqlMessageStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SqlRunnerStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__TestRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Client.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Domain.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Server.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Event.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventJournal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLog.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogRemote.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogServer__Cloudflare.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__Procedure.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__ProcedureList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__SerializableProcedureList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedCache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedQueue__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence__Lmdb.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RateLimiter__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Reactivity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RequestResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Sse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__VariantSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Logger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Metrics.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__NodeSdk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Otlp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpLogger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpMetrics.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpResource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpTracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Resource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Tracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__WebSdk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserWorkerRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Clipboard.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Geolocation.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Permissions.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpPlatform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpServerRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunKeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSocket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSocketServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunStream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeMultipart.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeSink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeSocket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeStream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeHttpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeKeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__Undici.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__ChannelSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Command.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__CommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Cookies.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Effectify.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Error.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Etag.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__FetchHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__FileSystem.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Headers.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiBuilder.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiEndpoint.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiScalar.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSecurity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSwagger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpBody.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientResponse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpIncomingMessage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpLayerRouter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMethod.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMultiplex.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpPlatform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpRouter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerRespondable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerResponse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__KeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__MsgPack.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Multipart.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Ndjson.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__OpenApi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__OpenApiJsonSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Path.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__PlatformConfigProvider.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__PlatformLogger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Runtime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Socket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__SocketServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Template.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Terminal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Transferable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Url.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__UrlParams.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Worker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__WorkerError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__WorkerRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__Ansi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__AnsiDoc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__Color.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Doc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__DocStream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__DocTree.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Flatten.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Layout.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Optimize.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__PageWidth.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__Rpc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcClientError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcMessage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcSerialization.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcTest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__ClickhouseClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-d1__D1Client.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-d1__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Mysql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Pg.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Sqlite.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Kysely.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Mssql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Mysql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Pg.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Sqlite.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__patch.types.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-libsql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__MssqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__Parameter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__Procedure.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mysql2__MysqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mysql2__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-pg__PgClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-pg__index.yaml (100%) create mode 100644 migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-bun__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-do__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-do__index.yaml (100%) create mode 100644 migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-node__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-react-native__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-wasm__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Migrator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Migrator__FileSystem.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Model.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlConnection.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlEventJournal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlEventLogServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlPersistedQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Statement.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Alternative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Applicative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Bicovariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Bounded.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Chainable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Contravariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Coproduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Covariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Filterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__FlatMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Foldable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Invariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Monad.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Monoid.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Of.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Pointed.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Product.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiAlternative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiApplicative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiCoproduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiProduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Semigroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Traversable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__TraversableFilterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Array.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__BigInt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Boolean.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Duration.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Effect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Either.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Identity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Micro.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Number.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Option.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Ordering.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Predicate.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Record.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__String.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Tuple.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__vitest__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__vitest__utils.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__Activity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableClock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableDeferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableRateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__Workflow.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowEngine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowProxy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowProxyServer.yaml (100%) rename {repos/effect/migration => migration}/cause.md (100%) rename {repos/effect/migration => migration}/equality.md (100%) rename {repos/effect/migration => migration}/error-handling.md (100%) rename {repos/effect/migration => migration}/fiber-keep-alive.md (100%) rename {repos/effect/migration => migration}/fiberref.md (100%) rename {repos/effect/migration => migration}/forking.md (100%) rename {repos/effect/migration => migration}/generators.md (100%) rename {repos/effect/migration => migration}/layer-memoization.md (100%) rename {repos/effect/migration => migration}/runtime.md (100%) rename {repos/effect/migration => migration}/schema.md (100%) rename {repos/effect/migration => migration}/scope.md (100%) rename {repos/effect/migration => migration}/services.md (100%) rename {repos/effect/migration => migration}/v3-to-v4.md (99%) rename {repos/effect/migration => migration}/yieldable.md (100%) delete mode 100644 old.patch rename {repos/effect/packages => packages}/ai/anthropic/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/ai/anthropic/README.md (100%) rename {repos/effect/packages => packages}/ai/anthropic/codegen.yml (100%) rename {repos/effect/packages => packages}/ai/anthropic/package.json (98%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicClient.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicConfig.ts (96%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicError.ts (98%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicLanguageModel.ts (99%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicTelemetry.ts (87%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicTool.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/Generated.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/index.ts (88%) rename {repos/effect/packages => packages}/ai/anthropic/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/test/AnthropicClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/test/AnthropicLanguageModel.test.ts (91%) rename {repos/effect/packages => packages}/ai/anthropic/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts (100%) create mode 100644 packages/ai/cloudflare/CHANGELOG.md rename {repos/effect/packages => packages}/ai/cloudflare/README.md (100%) rename {repos/effect/packages => packages}/ai/cloudflare/package.json (98%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareClient.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareDecisionModel.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareSchema.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/src/index.ts (94%) rename {repos/effect/packages => packages}/ai/cloudflare/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/test/CloudflareClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/test/CloudflareDecisionModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/ai/openai-compat/README.md (100%) rename {repos/effect/packages => packages}/ai/openai-compat/package.json (98%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiClient.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiConfig.ts (96%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiEmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiError.ts (98%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiLanguageModel.ts (99%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiTelemetry.ts (68%) rename {repos/effect/packages => packages}/ai/openai-compat/src/index.ts (87%) rename {repos/effect/packages => packages}/ai/openai-compat/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiLanguageModel.test.ts (100%) create mode 100644 packages/ai/openai-compat/test/OpenAiTelemetry.test.ts rename {repos/effect/packages => packages}/ai/openai-compat/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/openai-compat/typetest/OpenAiConfig.tst.ts (100%) rename {repos/effect/packages => packages}/ai/openai/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/ai/openai/README.md (100%) rename {repos/effect/packages => packages}/ai/openai/codegen.yaml (100%) rename {repos/effect/packages => packages}/ai/openai/package.json (98%) rename {repos/effect/packages => packages}/ai/openai/src/Generated.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiClient.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiClientGenerated.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiConfig.ts (96%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiEmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiError.ts (98%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiLanguageModel.ts (99%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiSchema.ts (99%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiTelemetry.ts (70%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiTool.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/index.ts (91%) rename {repos/effect/packages => packages}/ai/openai/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiEmbeddingModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiLanguageModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiSchema.test.ts (100%) create mode 100644 packages/ai/openai/test/OpenAiTelemetry.test.ts rename {repos/effect/packages => packages}/ai/openai/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/openai/typetest/OpenAiTool.tst.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/ai/openrouter/README.md (100%) rename {repos/effect/packages => packages}/ai/openrouter/codegen.yml (100%) rename {repos/effect/packages => packages}/ai/openrouter/package.json (98%) rename {repos/effect/packages => packages}/ai/openrouter/src/Generated.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterClient.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterConfig.ts (96%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterDecisionModel.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterError.ts (98%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterLanguageModel.ts (99%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterSchema.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/index.ts (91%) rename {repos/effect/packages => packages}/ai/openrouter/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/Generated.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterDecisionModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterLanguageModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterSchema.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/CHANGELOG.md (75%) rename {repos/effect/packages => packages}/ai/typesafe/README.md (100%) rename {repos/effect/packages => packages}/ai/typesafe/package.json (98%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeClient.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeConfig.ts (95%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeDecisionModel.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeSchema.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/index.ts (90%) rename {repos/effect/packages => packages}/ai/typesafe/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeDecisionModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeSchema.test.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/atom/react/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/atom/react/LICENSE (100%) rename {repos/effect/packages => packages}/atom/react/README.md (100%) rename {repos/effect/packages => packages}/atom/react/package.json (98%) rename {repos/effect/packages => packages}/atom/react/src/Hooks.ts (87%) rename {repos/effect/packages => packages}/atom/react/src/ReactHydration.ts (98%) rename {repos/effect/packages => packages}/atom/react/src/RegistryContext.ts (98%) rename {repos/effect/packages => packages}/atom/react/src/ScopedAtom.ts (97%) rename {repos/effect/packages => packages}/atom/react/src/index.ts (91%) rename {repos/effect/packages => packages}/atom/react/test/index.test.tsx (84%) rename {repos/effect/packages => packages}/atom/react/tsconfig.json (100%) rename {repos/effect/packages => packages}/atom/react/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/atom/solid/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/atom/solid/LICENSE (100%) rename {repos/effect/packages => packages}/atom/solid/README.md (100%) rename {repos/effect/packages => packages}/atom/solid/package.json (98%) rename {repos/effect/packages => packages}/atom/solid/src/Hooks.ts (97%) rename {repos/effect/packages => packages}/atom/solid/src/RegistryContext.ts (97%) rename {repos/effect/packages => packages}/atom/solid/src/index.ts (85%) rename {repos/effect/packages => packages}/atom/solid/test/index.test.tsx (100%) rename {repos/effect/packages => packages}/atom/solid/tsconfig.json (100%) rename {repos/effect/packages => packages}/atom/solid/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/atom/vue/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/atom/vue/LICENSE (100%) rename {repos/effect/packages => packages}/atom/vue/README.md (100%) rename {repos/effect/packages => packages}/atom/vue/package.json (98%) rename {repos/effect/packages => packages}/atom/vue/src/index.ts (86%) rename {repos/effect/packages => packages}/atom/vue/test/index.test.ts (100%) rename {repos/effect/packages => packages}/atom/vue/tsconfig.json (100%) delete mode 100644 packages/cli/.gitignore delete mode 100644 packages/cli/.madgerc delete mode 100644 packages/cli/.npmignore delete mode 100644 packages/cli/CHANGELOG.md delete mode 100755 packages/cli/bin.js delete mode 100644 packages/cli/package.json delete mode 100644 packages/cli/src/argv-patch.ts delete mode 100644 packages/cli/src/extract.ts delete mode 100644 packages/cli/src/gist.ts delete mode 100644 packages/cli/src/index.ts delete mode 100644 packages/cli/src/os-command.ts delete mode 100644 packages/cli/src/shared.ts delete mode 100644 packages/cli/src/sync-effect-app-subtree.ts delete mode 100644 packages/cli/src/sync-effect-subtree.ts delete mode 100644 packages/cli/src/sync-shared.ts delete mode 100644 packages/cli/src/update-packages.ts delete mode 100644 packages/cli/test.gists.yaml delete mode 100644 packages/cli/test/argv-patch.test.ts delete mode 100644 packages/cli/test/gist-cache.test.ts delete mode 100644 packages/cli/test/update-packages.test.ts delete mode 100644 packages/cli/tsconfig.json delete mode 100644 packages/cli/vitest.config.ts delete mode 100644 packages/e2e/CHANGELOG.md delete mode 100644 packages/e2e/package.json delete mode 100644 packages/e2e/test/repoInvalidation.e2e.test.ts delete mode 100644 packages/e2e/tsconfig.json delete mode 100644 packages/e2e/vitest.config.ts delete mode 100644 packages/effect-app/.madgerc delete mode 100644 packages/effect-app/.npmignore delete mode 100644 packages/effect-app/CHANGELOG.md delete mode 100644 packages/effect-app/package.json delete mode 100644 packages/effect-app/src/Array.ts delete mode 100644 packages/effect-app/src/Chunk.ts delete mode 100644 packages/effect-app/src/Config.ts delete mode 100644 packages/effect-app/src/Config/SecretURL.ts delete mode 100644 packages/effect-app/src/Config/internal/configSecretURL.ts delete mode 100644 packages/effect-app/src/ConfigProvider.ts delete mode 100644 packages/effect-app/src/Context.ts delete mode 100644 packages/effect-app/src/DataDependencies.ts delete mode 100644 packages/effect-app/src/Effect.ts delete mode 100644 packages/effect-app/src/Emailer.ts delete mode 100644 packages/effect-app/src/Function.ts delete mode 100644 packages/effect-app/src/Inputify.type.ts delete mode 100644 packages/effect-app/src/Layer.ts delete mode 100644 packages/effect-app/src/Model.ts delete mode 100644 packages/effect-app/src/Model/Repository.ts delete mode 100644 packages/effect-app/src/Model/Repository/Registry.ts delete mode 100644 packages/effect-app/src/Model/Repository/dependency.ts delete mode 100644 packages/effect-app/src/Model/Repository/ext.ts delete mode 100644 packages/effect-app/src/Model/Repository/internal/internal.ts delete mode 100644 packages/effect-app/src/Model/Repository/legacy.ts delete mode 100644 packages/effect-app/src/Model/Repository/makeRepo.ts delete mode 100644 packages/effect-app/src/Model/Repository/service.ts delete mode 100644 packages/effect-app/src/Model/Repository/validation.ts delete mode 100644 packages/effect-app/src/Model/dsl.ts delete mode 100644 packages/effect-app/src/Model/filter/filterApi.ts delete mode 100644 packages/effect-app/src/Model/filter/types.ts delete mode 100644 packages/effect-app/src/Model/filter/types/errors.ts delete mode 100644 packages/effect-app/src/Model/filter/types/fields.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/common.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/eager.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/index.ts delete mode 100644 packages/effect-app/src/Model/filter/types/utils.ts delete mode 100644 packages/effect-app/src/Model/filter/types/validator.ts delete mode 100644 packages/effect-app/src/Model/query.ts delete mode 100644 packages/effect-app/src/Model/query/dsl.ts delete mode 100644 packages/effect-app/src/Model/query/new-kid-interpreter.ts delete mode 100644 packages/effect-app/src/NonEmptySet.ts delete mode 100644 packages/effect-app/src/Option.ts delete mode 100644 packages/effect-app/src/Pure.ts delete mode 100644 packages/effect-app/src/QueueMaker.ts delete mode 100644 packages/effect-app/src/RequestContext.ts delete mode 100644 packages/effect-app/src/RequestScopedDependencies.ts delete mode 100644 packages/effect-app/src/Schema.ts delete mode 100644 packages/effect-app/src/Schema/Class.ts delete mode 100644 packages/effect-app/src/Schema/FastCheck.ts delete mode 100644 packages/effect-app/src/Schema/Methods.ts delete mode 100644 packages/effect-app/src/Schema/SchemaParser.ts delete mode 100644 packages/effect-app/src/Schema/SpecialJsonSchema.ts delete mode 100644 packages/effect-app/src/Schema/SpecialOpenApi.ts delete mode 100644 packages/effect-app/src/Schema/brand.ts delete mode 100644 packages/effect-app/src/Schema/email.ts delete mode 100644 packages/effect-app/src/Schema/ext.ts delete mode 100644 packages/effect-app/src/Schema/moreStrings.ts delete mode 100644 packages/effect-app/src/Schema/numbers.ts delete mode 100644 packages/effect-app/src/Schema/phoneNumber.ts delete mode 100644 packages/effect-app/src/Schema/schema.ts delete mode 100644 packages/effect-app/src/Schema/strings.ts delete mode 100644 packages/effect-app/src/SchemaAST.ts delete mode 100644 packages/effect-app/src/Set.ts delete mode 100644 packages/effect-app/src/Store.ts delete mode 100644 packages/effect-app/src/TypeTest.ts delete mode 100644 packages/effect-app/src/Types.ts delete mode 100644 packages/effect-app/src/Widen.type.ts delete mode 100644 packages/effect-app/src/_ext/Array.ts delete mode 100644 packages/effect-app/src/_ext/date.ts delete mode 100644 packages/effect-app/src/_ext/misc.ts delete mode 100644 packages/effect-app/src/_ext/ord.ext.ts delete mode 100644 packages/effect-app/src/builtin-json.d.ts delete mode 100644 packages/effect-app/src/builtin.ts delete mode 100644 packages/effect-app/src/client.ts delete mode 100644 packages/effect-app/src/client/InvalidationKeys.ts delete mode 100644 packages/effect-app/src/client/apiClientFactory.ts delete mode 100644 packages/effect-app/src/client/clientFor.ts delete mode 100644 packages/effect-app/src/client/errors.ts delete mode 100644 packages/effect-app/src/client/makeClient.ts delete mode 100644 packages/effect-app/src/faker.ts delete mode 100644 packages/effect-app/src/http.ts delete mode 100644 packages/effect-app/src/http/Request.ts delete mode 100644 packages/effect-app/src/http/internal/lib.ts delete mode 100644 packages/effect-app/src/ids.ts delete mode 100644 packages/effect-app/src/index.ts delete mode 100644 packages/effect-app/src/logger.ts delete mode 100644 packages/effect-app/src/middleware.ts delete mode 100644 packages/effect-app/src/rpc.ts delete mode 100644 packages/effect-app/src/rpc/Invalidation.ts delete mode 100644 packages/effect-app/src/rpc/MiddlewareMaker.ts delete mode 100644 packages/effect-app/src/rpc/README.md delete mode 100644 packages/effect-app/src/rpc/RpcContextMap.ts delete mode 100644 packages/effect-app/src/rpc/RpcMiddleware.ts delete mode 100644 packages/effect-app/src/runtime.ts delete mode 100644 packages/effect-app/src/setupRequest.ts delete mode 100644 packages/effect-app/src/toast.ts delete mode 100644 packages/effect-app/src/transform.ts delete mode 100644 packages/effect-app/src/utils.ts delete mode 100644 packages/effect-app/src/utils/effectify.ts delete mode 100644 packages/effect-app/src/utils/extend.ts delete mode 100644 packages/effect-app/src/utils/gen.ts delete mode 100644 packages/effect-app/src/utils/logLevel.ts delete mode 100644 packages/effect-app/src/utils/logger.ts delete mode 100644 packages/effect-app/src/validation.ts delete mode 100644 packages/effect-app/src/validation/validators.ts delete mode 100644 packages/effect-app/src/withToast.ts delete mode 100644 packages/effect-app/test/moreStrings.test.ts delete mode 100644 packages/effect-app/test/rpc.test.ts delete mode 100644 packages/effect-app/test/rpcMiddlewareContext.test.ts delete mode 100644 packages/effect-app/test/schema.test.ts delete mode 100644 packages/effect-app/test/secretURL.test.ts delete mode 100644 packages/effect-app/test/special.test.ts delete mode 100644 packages/effect-app/test/utils.test.ts delete mode 100644 packages/effect-app/tsconfig.base.json delete mode 100644 packages/effect-app/tsconfig.json delete mode 100644 packages/effect-app/tsconfig.src.json delete mode 100644 packages/effect-app/tsconfig.test.json delete mode 100644 packages/effect-app/vitest.config.ts rename {repos/effect/packages => packages}/effect/ARBITRARY.md (100%) rename {repos/effect/packages => packages}/effect/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/effect/CONFIG.md (100%) rename {repos/effect/packages => packages}/effect/HTTPAPI.md (100%) rename {repos/effect/packages => packages}/effect/LICENSE (100%) rename {repos/effect/packages => packages}/effect/MCP.md (100%) rename {repos/effect/packages => packages}/effect/OPTIC.md (100%) rename {repos/effect/packages => packages}/effect/README.md (100%) rename {repos/effect/packages => packages}/effect/SCHEMA.md (100%) rename {repos/effect/packages => packages}/effect/benchmark/Pool.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/multipart.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/serverAllocations.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/tracer.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/httpapi/sseClientDecoder.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/net/NetAddress.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/rpc/RpcSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/Optic.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaBinary.md (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaBinary.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaError.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/stream/splitLines.ts (100%) rename {repos/effect/packages => packages}/effect/package.json (97%) rename {repos/effect/packages => packages}/effect/runtimeperf/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/compare.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/config.json (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/materialize.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/run.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/stats.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/native.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/costs.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/data.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/generate.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/jit.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/zod.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-checked-sources/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/RESULTS.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/TO-TYPE.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/fixtures.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/profile.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/run.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/worker.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/adapters.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/behavior.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/cold.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/comparison.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/materialize.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/registry.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/schema-construction.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/stats.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/worker.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/worktree-diff.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/utils.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/worker.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/worktree-diff.mts (100%) rename {repos/effect/packages => packages}/effect/src/Arbitrary.ts (100%) rename {repos/effect/packages => packages}/effect/src/Array.ts (97%) rename {repos/effect/packages => packages}/effect/src/BigDecimal.ts (98%) rename {repos/effect/packages => packages}/effect/src/BigInt.ts (97%) rename {repos/effect/packages => packages}/effect/src/Boolean.ts (96%) rename {repos/effect/packages => packages}/effect/src/Brand.ts (97%) rename {repos/effect/packages => packages}/effect/src/ByteSize.ts (94%) rename {repos/effect/packages => packages}/effect/src/Cache.ts (97%) rename {repos/effect/packages => packages}/effect/src/Cause.ts (96%) rename {repos/effect/packages => packages}/effect/src/Channel.ts (98%) rename {repos/effect/packages => packages}/effect/src/ChannelSchema.ts (98%) rename {repos/effect/packages => packages}/effect/src/Chunk.ts (97%) rename {repos/effect/packages => packages}/effect/src/Clock.ts (98%) rename {repos/effect/packages => packages}/effect/src/Combiner.ts (97%) rename {repos/effect/packages => packages}/effect/src/Config.ts (95%) rename {repos/effect/packages => packages}/effect/src/ConfigProvider.ts (98%) rename {repos/effect/packages => packages}/effect/src/Console.ts (98%) rename {repos/effect/packages => packages}/effect/src/Context.ts (98%) rename {repos/effect/packages => packages}/effect/src/Cron.ts (97%) rename {repos/effect/packages => packages}/effect/src/Crypto.ts (98%) rename {repos/effect/packages => packages}/effect/src/Data.ts (99%) rename {repos/effect/packages => packages}/effect/src/DateTime.ts (97%) rename {repos/effect/packages => packages}/effect/src/Deferred.ts (98%) rename {repos/effect/packages => packages}/effect/src/Differ.ts (95%) rename {repos/effect/packages => packages}/effect/src/Duration.ts (97%) rename {repos/effect/packages => packages}/effect/src/Effect.ts (98%) rename {repos/effect/packages => packages}/effect/src/Effectable.ts (98%) rename {repos/effect/packages => packages}/effect/src/Equal.ts (98%) rename {repos/effect/packages => packages}/effect/src/Equivalence.ts (98%) rename {repos/effect/packages => packages}/effect/src/ErrorReporter.ts (95%) rename {repos/effect/packages => packages}/effect/src/ExecutionPlan.ts (85%) rename {repos/effect/packages => packages}/effect/src/Exit.ts (97%) rename {repos/effect/packages => packages}/effect/src/Fiber.ts (98%) rename {repos/effect/packages => packages}/effect/src/FiberHandle.ts (97%) rename {repos/effect/packages => packages}/effect/src/FiberMap.ts (98%) rename {repos/effect/packages => packages}/effect/src/FiberSet.ts (98%) rename {repos/effect/packages => packages}/effect/src/FileSystem.ts (98%) rename {repos/effect/packages => packages}/effect/src/Filter.ts (97%) rename {repos/effect/packages => packages}/effect/src/Formatter.ts (87%) rename {repos/effect/packages => packages}/effect/src/Function.ts (98%) rename {repos/effect/packages => packages}/effect/src/Graph.ts (97%) rename {repos/effect/packages => packages}/effect/src/HKT.ts (98%) rename {repos/effect/packages => packages}/effect/src/Hash.ts (98%) rename {repos/effect/packages => packages}/effect/src/HashMap.ts (97%) rename {repos/effect/packages => packages}/effect/src/HashRing.ts (98%) rename {repos/effect/packages => packages}/effect/src/HashSet.ts (97%) rename {repos/effect/packages => packages}/effect/src/Inspectable.ts (98%) rename {repos/effect/packages => packages}/effect/src/Iterable.ts (98%) rename {repos/effect/packages => packages}/effect/src/JsonPatch.ts (99%) rename {repos/effect/packages => packages}/effect/src/JsonPointer.ts (98%) rename {repos/effect/packages => packages}/effect/src/JsonSchema.ts (99%) rename {repos/effect/packages => packages}/effect/src/Latch.ts (97%) rename {repos/effect/packages => packages}/effect/src/Layer.ts (98%) rename {repos/effect/packages => packages}/effect/src/LayerMap.ts (99%) rename {repos/effect/packages => packages}/effect/src/LayerRef.ts (98%) rename {repos/effect/packages => packages}/effect/src/LogLevel.ts (97%) rename {repos/effect/packages => packages}/effect/src/Logger.ts (97%) rename {repos/effect/packages => packages}/effect/src/ManagedRuntime.ts (95%) rename {repos/effect/packages => packages}/effect/src/Match.ts (98%) rename {repos/effect/packages => packages}/effect/src/Metric.ts (99%) rename {repos/effect/packages => packages}/effect/src/MutableHashMap.ts (98%) rename {repos/effect/packages => packages}/effect/src/MutableHashSet.ts (98%) rename {repos/effect/packages => packages}/effect/src/MutableList.ts (97%) rename {repos/effect/packages => packages}/effect/src/MutableRef.ts (98%) rename {repos/effect/packages => packages}/effect/src/Newtype.ts (97%) rename {repos/effect/packages => packages}/effect/src/NonEmptyIterable.ts (97%) rename {repos/effect/packages => packages}/effect/src/Number.ts (96%) rename {repos/effect/packages => packages}/effect/src/Optic.ts (99%) rename {repos/effect/packages => packages}/effect/src/Option.ts (98%) rename {repos/effect/packages => packages}/effect/src/Order.ts (98%) rename {repos/effect/packages => packages}/effect/src/Ordering.ts (97%) rename {repos/effect/packages => packages}/effect/src/PartitionedSemaphore.ts (91%) rename {repos/effect/packages => packages}/effect/src/Path.ts (99%) rename {repos/effect/packages => packages}/effect/src/Pipeable.ts (98%) rename {repos/effect/packages => packages}/effect/src/PlatformError.ts (97%) rename {repos/effect/packages => packages}/effect/src/Pool.ts (99%) rename {repos/effect/packages => packages}/effect/src/Predicate.ts (97%) rename {repos/effect/packages => packages}/effect/src/PrimaryKey.ts (97%) rename {repos/effect/packages => packages}/effect/src/PubSub.ts (96%) rename {repos/effect/packages => packages}/effect/src/Pull.ts (97%) rename {repos/effect/packages => packages}/effect/src/Queue.ts (97%) rename {repos/effect/packages => packages}/effect/src/Random.ts (98%) rename {repos/effect/packages => packages}/effect/src/RcMap.ts (99%) rename {repos/effect/packages => packages}/effect/src/RcRef.ts (98%) rename {repos/effect/packages => packages}/effect/src/Record.ts (97%) rename {repos/effect/packages => packages}/effect/src/Redactable.ts (97%) rename {repos/effect/packages => packages}/effect/src/Redacted.ts (98%) rename {repos/effect/packages => packages}/effect/src/Reducer.ts (98%) rename {repos/effect/packages => packages}/effect/src/Ref.ts (98%) rename {repos/effect/packages => packages}/effect/src/References.ts (97%) rename {repos/effect/packages => packages}/effect/src/RegExp.ts (96%) rename {repos/effect/packages => packages}/effect/src/Request.ts (97%) rename {repos/effect/packages => packages}/effect/src/RequestResolver.ts (97%) rename {repos/effect/packages => packages}/effect/src/Resource.ts (97%) rename {repos/effect/packages => packages}/effect/src/Result.ts (98%) rename {repos/effect/packages => packages}/effect/src/Runtime.ts (98%) rename {repos/effect/packages => packages}/effect/src/Schedule.ts (98%) rename {repos/effect/packages => packages}/effect/src/Scheduler.ts (98%) rename {repos/effect/packages => packages}/effect/src/Schema.ts (98%) rename {repos/effect/packages => packages}/effect/src/SchemaAST.ts (98%) rename {repos/effect/packages => packages}/effect/src/SchemaGetter.ts (97%) rename {repos/effect/packages => packages}/effect/src/SchemaIssue.ts (98%) rename {repos/effect/packages => packages}/effect/src/SchemaParser.ts (98%) rename {repos/effect/packages => packages}/effect/src/SchemaRepresentation.ts (96%) rename {repos/effect/packages => packages}/effect/src/SchemaTransformation.ts (98%) rename {repos/effect/packages => packages}/effect/src/Scope.ts (98%) rename {repos/effect/packages => packages}/effect/src/ScopedCache.ts (96%) rename {repos/effect/packages => packages}/effect/src/ScopedRef.ts (97%) rename {repos/effect/packages => packages}/effect/src/Semaphore.ts (98%) rename {repos/effect/packages => packages}/effect/src/Sink.ts (97%) rename {repos/effect/packages => packages}/effect/src/StandardSchema.ts (99%) rename {repos/effect/packages => packages}/effect/src/Stdio.ts (96%) rename {repos/effect/packages => packages}/effect/src/Stream.ts (98%) rename {repos/effect/packages => packages}/effect/src/String.ts (96%) rename {repos/effect/packages => packages}/effect/src/Struct.ts (98%) rename {repos/effect/packages => packages}/effect/src/SubscriptionRef.ts (98%) rename {repos/effect/packages => packages}/effect/src/Symbol.ts (95%) rename {repos/effect/packages => packages}/effect/src/SynchronizedRef.ts (97%) rename {repos/effect/packages => packages}/effect/src/Take.ts (96%) rename {repos/effect/packages => packages}/effect/src/Terminal.ts (96%) rename {repos/effect/packages => packages}/effect/src/Tracer.ts (97%) rename {repos/effect/packages => packages}/effect/src/Trie.ts (97%) rename {repos/effect/packages => packages}/effect/src/Tuple.ts (98%) rename {repos/effect/packages => packages}/effect/src/TxChunk.ts (97%) rename {repos/effect/packages => packages}/effect/src/TxDeferred.ts (98%) rename {repos/effect/packages => packages}/effect/src/TxHashMap.ts (98%) rename {repos/effect/packages => packages}/effect/src/TxHashSet.ts (97%) rename {repos/effect/packages => packages}/effect/src/TxPriorityQueue.ts (96%) rename {repos/effect/packages => packages}/effect/src/TxPubSub.ts (98%) rename {repos/effect/packages => packages}/effect/src/TxQueue.ts (98%) rename {repos/effect/packages => packages}/effect/src/TxReentrantLock.ts (93%) rename {repos/effect/packages => packages}/effect/src/TxRef.ts (97%) rename {repos/effect/packages => packages}/effect/src/TxSemaphore.ts (94%) rename {repos/effect/packages => packages}/effect/src/TxSubscriptionRef.ts (98%) rename {repos/effect/packages => packages}/effect/src/Types.ts (97%) rename {repos/effect/packages => packages}/effect/src/UndefinedOr.ts (97%) rename {repos/effect/packages => packages}/effect/src/Unify.ts (97%) rename {repos/effect/packages => packages}/effect/src/Utils.ts (98%) create mode 100644 packages/effect/src/Version.ts rename {repos/effect/packages => packages}/effect/src/ai/AiError.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/AnthropicStructuredOutput.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Chat.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/Decision.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/DecisionModel.ts (90%) rename {repos/effect/packages => packages}/effect/src/ai/EmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/IdGenerator.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/LanguageModel.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/McpProtocol.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/McpSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/McpServer.ts (95%) rename {repos/effect/packages => packages}/effect/src/ai/Model.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/OpenAiStructuredOutput.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Prompt.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Response.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/ResponseIdTracker.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Telemetry.ts (86%) rename {repos/effect/packages => packages}/effect/src/ai/Tokenizer.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Tool.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Toolkit.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/codec-transformer.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpCore.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocolRegistry.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpRuntime.ts (86%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2024_11_05.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_03_26.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_06_18.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_11_25.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2026_07_28.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpStatefulRuntime.ts (98%) rename {repos/effect/packages => packages}/effect/src/ai/internal/structured-output.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Argument.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliConfig.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliError.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliOutput.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Command.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Completions.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Flag.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/GlobalFlag.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/HelpDoc.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Param.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Primitive.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Prompt.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/ansi.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/auto-suggest.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/command.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/bash.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/descriptor.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/fish.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/zsh.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/config.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/help.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/lexer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/parser.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/wizard.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterCron.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterError.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterMetrics.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterWorkflowEngine.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/DeliverAt.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Entity.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityProxy.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityProxyServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityResource.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityType.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Envelope.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/HttpRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/K8sHttpClient.ts (96%) rename {repos/effect/packages => packages}/effect/src/cluster/K8sTypes.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/MachineId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Message.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/MessageStorage.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Reply.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Runner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerHealth.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerStorage.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Runners.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Sharding.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardingConfig.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardingRegistrationEvent.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SingleRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Singleton.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SingletonAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Snowflake.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SocketRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SqlMessageStorage.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SqlRunnerStorage.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/TestRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/clusterAbandon.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityActivation.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityManager.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityReaper.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/hash.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/interruptors.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/resourceMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/resourceRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/shardLock.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevTools.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsClient.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Base64.ts (98%) rename {repos/effect/packages => packages}/effect/src/encoding/Base64Url.ts (97%) rename {repos/effect/packages => packages}/effect/src/encoding/EncodingError.ts (96%) rename {repos/effect/packages => packages}/effect/src/encoding/Hex.ts (98%) rename {repos/effect/packages => packages}/effect/src/encoding/Ini.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Ndjson.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/SchemaBinary.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Sse.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Toml.ts (98%) rename {repos/effect/packages => packages}/effect/src/encoding/Yaml.ts (97%) rename {repos/effect/packages => packages}/effect/src/encoding/index.ts (91%) rename {repos/effect/packages => packages}/effect/src/eventlog/Event.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventJournal.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLog.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogEncryption.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogRemote.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServerEncrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServerUnencrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogSessionAuth.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventJournal.ts (96%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventLogServerEncrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventLogServerUnencrypted.ts (56%) rename {repos/effect/packages => packages}/effect/src/eventlog/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/internal/identityRootSecretDerivation.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApi.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiBuilder.ts (99%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiClient.ts (98%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiEndpoint.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiError.ts (97%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiMiddleware.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiScalar.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSecurity.ts (93%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSwagger.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiTest.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/OpenApi.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/html.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/httpApiScalar.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/httpApiSwagger.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/mediaType.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/path.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Cookies.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Etag.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FetchHttpClient.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay/internal/queryString.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay/internal/router.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Headers.ts (97%) rename {repos/effect/packages => packages}/effect/src/http/HttpBody.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpClient.ts (96%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientError.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientRequest.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientResponse.ts (98%) rename {repos/effect/packages => packages}/effect/src/http/HttpEffect.ts (97%) rename {repos/effect/packages => packages}/effect/src/http/HttpIncomingMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpMethod.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpMiddleware.ts (88%) rename {repos/effect/packages => packages}/effect/src/http/HttpPlatform.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpRouter.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServer.ts (99%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerError.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerRequest.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerRespondable.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerResponse.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpStaticServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpStatus.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpTraceContext.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Mime.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Multipart.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/HeadersParser.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/Search.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/contentType.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/headers.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/multipart.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/search.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Template.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Url.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/UrlParams.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/compression.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/headers.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/httpBody.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/mimeTypes.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/preResponseHandler.ts (100%) create mode 100644 packages/effect/src/http/internal/tracing.ts rename {repos/effect/packages => packages}/effect/src/index.ts (74%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/array.ts (96%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/bigDecimal.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/bigInt.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/model.ts (95%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/regexp.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/runner.ts (98%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/schema.ts (99%) rename {repos/effect/packages => packages}/effect/src/internal/array.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/core.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/count.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/dateTime.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/doNotation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/effect.ts (99%) rename {repos/effect/packages => packages}/effect/src/internal/equal.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/executionPlan.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/graph.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/graphCsr.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/hash.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/hashMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/hashSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/layer.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/matcher.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/metric.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/option.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/persistence.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/random.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/rcRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/record.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/redacted.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/references.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/request.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/result.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schedule.ts (89%) rename {repos/effect/packages => packages}/effect/src/internal/schema/annotations.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/cause.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/codegen.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/compilerRegistry.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/fromJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/fromRepresentation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/interpreter.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/make.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/parser.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/standardSchema.ts (98%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toCodeDocument.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toCodec.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toDifferJsonPatch.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toEncoderXml.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toEquivalence.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toFormatter.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toIso.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toRepresentation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schemaError.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/stackTraceLimit.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/stream.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/tracer.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/trie.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/ulid.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/uuid.ts (100%) create mode 100644 packages/effect/src/internal/version.ts rename {repos/effect/packages => packages}/effect/src/net/IpInterface.ts (100%) rename {repos/effect/packages => packages}/effect/src/net/IpNetwork.ts (100%) rename {repos/effect/packages => packages}/effect/src/net/NetAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/net/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/Otlp.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpExporter.ts (95%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpLogger.ts (90%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpMetrics.ts (88%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpResource.ts (89%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpTracer.ts (92%) rename {repos/effect/packages => packages}/effect/src/observability/PrometheusMetrics.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/internal/otlpEnv.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/internal/otlpProtobuf.ts (91%) rename {repos/effect/packages => packages}/effect/src/observability/internal/protobuf.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/KeyValueStore.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/Persistable.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/PersistedCache.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/PersistedQueue.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/Persistence.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/RateLimiter.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/Redis.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/ChildProcess.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/ChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AsyncResult.ts (99%) rename {repos/effect/packages => packages}/effect/src/reactivity/Atom.ts (99%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomHttpApi.ts (98%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRegistry.ts (99%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRpc.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/Hydration.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/Reactivity.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/Rpc.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcClient.ts (95%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcClientError.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcMiddleware.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcServer.ts (97%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcTest.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcWorker.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/Utils.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/index.ts (100%) create mode 100644 packages/effect/src/rpc/internal/tracing.ts rename {repos/effect/packages => packages}/effect/src/schema/Model.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaAOTCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaAOTCompiler/Build.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaCompiler/runtime.ts (97%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaJITCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaJITCompiler/enable.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/VariantSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/socket/Socket.ts (100%) rename {repos/effect/packages => packages}/effect/src/socket/SocketServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/socket/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/Migrator.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlClient.ts (98%) rename {repos/effect/packages => packages}/effect/src/sql/SqlConnection.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlError.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlModel.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlResolver.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlStream.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/Statement.ts (95%) rename {repos/effect/packages => packages}/effect/src/sql/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/testing/TestClock.ts (98%) rename {repos/effect/packages => packages}/effect/src/testing/TestConsole.ts (98%) rename {repos/effect/packages => packages}/effect/src/testing/TestSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/testing/index.ts (82%) rename {repos/effect/packages => packages}/effect/src/workers/Transferable.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/Worker.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/WorkerError.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/WorkerRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/Activity.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableClock.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableDeferred.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableQueue.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/Workflow.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowEngine.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowProxy.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowProxyServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/internal/crypto.ts (100%) rename {repos/effect/packages => packages}/effect/test/Array.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/ArrayNaNIndex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/AtomRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/BigDecimal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/BigInt.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Boolean.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Brand.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ByteSize.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Cache.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/Cause.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/CauseMapAnnotations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Channel.test.ts (88%) rename {repos/effect/packages => packages}/effect/test/Chunk.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Clock.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Combiner.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Config.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/ConfigProvider.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Context.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Cron.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/Crypto.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Data.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/DateTime.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Deferred.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Duration.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Effect.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/EffectEager.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/EffectKeepAlive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Effectable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Equal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Equivalence.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ExecutionPlan.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/Exit.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Fiber.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/FiberHandle.test.ts (89%) rename {repos/effect/packages => packages}/effect/test/FiberMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberSetRuntimePropagation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FileSystem.test-utils.ts (100%) rename {repos/effect/packages => packages}/effect/test/Formatter.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/Function.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Graph.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/Hash.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashRing.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HttpClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Iterable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonPatch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonPointer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Latch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Layer.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/LayerMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/LayerRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/LogLevel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Logger.test.ts (78%) rename {repos/effect/packages => packages}/effect/test/ManagedRuntime.test.ts (65%) rename {repos/effect/packages => packages}/effect/test/Match.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Metric.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Migrator.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableHashMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableHashSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableList.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Newtype.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Number.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Optic.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Option.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Order.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Ordering.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/PartitionedSemaphore.test.ts (82%) rename {repos/effect/packages => packages}/effect/test/Pathfinding.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Pool.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/Predicate.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/PubSub.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/Pull.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Queue.test.ts (90%) rename {repos/effect/packages => packages}/effect/test/Random.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/RcMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/RcRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Record.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Redacted.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Reducer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Ref.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Request.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/Resource.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Result.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Schedule.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Scheduler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Scope.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/ScopedCache.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/ScopedRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Semaphore.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Sink.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/StackCapture.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/StackTraceLimit.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Stdio.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Stream.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/String.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Struct.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SubscriptionRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Symbol.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SynchronizedRef.modifySomeEffect.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SynchronizedRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TestClock.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Tracer.test.ts (87%) rename {repos/effect/packages => packages}/effect/test/Trie.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Tuple.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxChunk.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxDeferred.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxHashMap.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/TxHashSet.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/TxPriorityQueue.test.ts (91%) rename {repos/effect/packages => packages}/effect/test/TxPubSub.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxReentrantLock.test.ts (91%) rename {repos/effect/packages => packages}/effect/test/TxSemaphore.test.ts (87%) rename {repos/effect/packages => packages}/effect/test/TxSubscriptionRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/UndefinedOr.test.ts (100%) create mode 100644 packages/effect/test/Version.test.ts rename {repos/effect/packages => packages}/effect/test/ai/AiError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/AnthropicStructuredOutput.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Chat.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/DecisionModel.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/ai/EmbeddingModel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModel.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModelRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModelTrackerLifecycle.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/CompletionTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/LoggingTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpConformance.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/PromptsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/RootsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/SamplingTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ToolsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/TransportsTest.ts (94%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpProtocol.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpServer.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/ProtocolAdapters.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts (90%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts (88%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2024_11_05.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_03_26.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_06_18.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_11_25.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2026_07_28.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Model.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/OpenAiStructuredOutput.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Prompt.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Response.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/ResponseIdTracker.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Tokenizer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Tool.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/ToolRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/utils.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.array.test.ts (100%) create mode 100644 packages/effect/test/arbitrary/Arbitrary.causes.test.ts rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.composition.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.configuration.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/cli/Ansi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Arguments.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Command.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Errors.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Help.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Lexer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/LogLevel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Param.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Primitive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Prompt.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/completions/completions.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/completions/descriptor.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/fixtures/ComprehensiveCli.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/services/MockTerminal.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/services/TestActions.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ClusterWorkflowEngine.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Entity.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/EntityProxy.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Envelope.test.ts (100%) create mode 100644 packages/effect/test/cluster/K8sHttpClient.test.ts rename {repos/effect/packages => packages}/effect/test/cluster/MessageStorage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ResourceMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ResourceRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/RunnerServer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/RunnerStorage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Runners.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Sharding.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ShardingConfig.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/TestEntity.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/interruptors.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/devtools/DevToolsClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Base64.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Base64Url.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/EncodingError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Hex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Ini.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Ndjson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/SchemaBinary.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Sse.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Toml.test.ts (78%) rename {repos/effect/packages => packages}/effect/test/encoding/Yaml.test.ts (91%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventJournal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLog.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogIdentityDerivation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogMessage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogRemote.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogServerUnencrypted.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogSessionAuth.test.ts (100%) create mode 100644 packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts rename {repos/effect/packages => packages}/effect/test/fixtures/migrator/0001_first.js (100%) rename {repos/effect/packages => packages}/effect/test/fixtures/text.txt (100%) rename {repos/effect/packages => packages}/effect/test/http/Cookies.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FetchHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/case-insensitive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/matching-order.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/methods.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/optional-params.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/params-collisions.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/path-params-match.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/querystring.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/regex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Headers.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpBody.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpClient.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/http/HttpClientRequest.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpEffect.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpMiddleware.test.ts (85%) rename {repos/effect/packages => packages}/effect/test/http/HttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpRouter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerRequest.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerRespondable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerResponse.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpStaticServer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpStatus.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpTraceContext.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Mime.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Multipart.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/SchemaRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Template.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Url.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/UrlParams.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiBuilder.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiDocumentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiEndpoint.test.ts (100%) create mode 100644 packages/effect/test/httpapi/HttpApiError.test.ts rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiSecurity.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/OpenApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/OpenApiRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/internal/Uuid.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/net/IpInterface.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/net/IpNetwork.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/net/NetAddress.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpConfig.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpEnvHeaders.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpExporter.test.ts (95%) create mode 100644 packages/effect/test/observability/OtlpLogger.test.ts rename {repos/effect/packages => packages}/effect/test/observability/OtlpMetrics.test.ts (79%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpResource.test.ts (74%) create mode 100644 packages/effect/test/observability/OtlpSerialization.test.ts create mode 100644 packages/effect/test/observability/OtlpTracer.test.ts rename {repos/effect/packages => packages}/effect/test/observability/PrometheusMetrics.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/KeyValueStoreTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedCache.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedCacheTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedQueueTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/RateLimiter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/RateLimiterTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/Redis.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/SqlCleanupTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/ChildProcess.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/ChildProcessSpawnerTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/no-permissions.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/parent-exits-early.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/spawn-children.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/SHOUTING (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/integer (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/nested/config (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/secret (100%) rename {repos/effect/packages => packages}/effect/test/reactivity/AsyncResult.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/reactivity/Atom.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/reactivity/AtomHttpApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/reactivity/AtomRpc.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/rpc/Rpc.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcClient.test.ts (76%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcSerialization.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcServer.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/schema/CodePoints.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/Graph.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/HMR.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/Schema.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAOTCompiler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAOTCompilerBuild.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAST.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerArray.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerConcurrency.test.ts (58%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerConstruction.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerRegression.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerStartup.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaGetter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaIssue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompiler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerFallback.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaParser.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaTransformation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/TemplateLiteral.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/VariantSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/HMR-Class.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/HMR-sanity-check.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-build.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-import-guard.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-runner.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/construction.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/jsonSchemaRoundTrip.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/builtInRevivers.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonSchemaDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromRepresentations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/makeCode.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/stringTreeRoundtrip.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toCodeDocument.annotations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toCodeDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonSchemaDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toRepresentations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toCodec.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toDifferJsonPatch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toEquivalence.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/schema/toFormatter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toIso.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toJsonSchemaDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toStandardJSONSchemaV1.test.ts (80%) rename {repos/effect/packages => packages}/effect/test/schema/toStandardSchemaV1.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/schema/v3-v4.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/socket/Socket.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlResolver.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/Statement.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/testing/TestSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/assert.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/chunkCoordination.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/counter.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/gc.ts (100%) rename {repos/effect/packages => packages}/effect/test/workers/Worker.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workers/WorkerError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/DurableQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/Workflow.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/WorkflowEngine.test.ts (100%) rename {repos/effect/packages => packages}/effect/tsconfig.json (100%) rename {repos/effect/packages => packages}/effect/typeperf/README.md (100%) rename {repos/effect/packages => packages}/effect/typeperf/compare.mjs (100%) rename {repos/effect/packages => packages}/effect/typeperf/compare/httpapi.mjs (100%) rename {repos/effect/packages => packages}/effect/typeperf/config.json (100%) rename {repos/effect/packages => packages}/effect/typeperf/run.mjs (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/baseline.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/thresholds.json (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/baseline.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/array.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/brand.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/exit.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/non-empty-array.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/record.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-mixed.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-required.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tagged-union.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-json.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-encoded.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-type.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tuple.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/union.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/thresholds.json (100%) rename {repos/effect/packages => packages}/effect/typetest/AiError.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Array.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Brand.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ByteSize.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Channel.tst.ts (70%) rename {repos/effect/packages => packages}/effect/typetest/ChannelSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Clock.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Config.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ConfigProvider.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Context.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Data.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Effect.tst.ts (95%) rename {repos/effect/packages => packages}/effect/typetest/Effectable.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Equivalence.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Fiber.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/FileSystem.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Function.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Graph.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Iterable.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Layer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Match.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/MigratedSubpaths.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Newtype.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Optic.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Option.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Order.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Predicate.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Queue.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Random.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Record.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Result.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Schedule.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Scope.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Sink.tst.ts (59%) rename {repos/effect/packages => packages}/effect/typetest/Stream.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Struct.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/SynchronizedRef.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/TestSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Tuple.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Types.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/VariantSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/DecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/LanguageModel.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/McpServer.tst.ts (80%) rename {repos/effect/packages => packages}/effect/typetest/ai/Response.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/Tool.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/Toolkit.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/arbitrary/Arbitrary.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Command.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Param.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Prompt.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/EntityProxyServer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/K8sHttpClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/Reply.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/encoding/Ndjson.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpPlatform.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpRouter.tst.ts (100%) create mode 100644 packages/effect/typetest/http/HttpServer.tst.ts rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApi.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiBuilder.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiClient.tst.ts (98%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiEndpoint.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiGroup.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiMiddleware.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/OpenApiRepresentation.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/net/IpInterface.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/net/IpNetwork.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/net/NetAddress.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/AsyncResult.tst.ts (89%) create mode 100644 packages/effect/typetest/reactivity/Atom.tst.ts rename {repos/effect/packages => packages}/effect/typetest/reactivity/AtomHttpApi.tst.ts (93%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/AtomRpc.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/Rpc.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcGroup.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcMessage.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcMiddleware.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcServer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Array.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/FromJsonSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Graph.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Literals.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Record.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Schema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaAOTCompiler.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaCompiler.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaGetter.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentation.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentationReviver.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Struct.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Tuple.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Union.tst.ts (98%) rename {repos/effect/packages => packages}/effect/typetest/schema/api.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/options.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/toIso.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/socket/Socket.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/sql/SqlSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/workflow/Workflow.tst.ts (100%) delete mode 100644 packages/eslint-codegen-model/CHANGELOG.md delete mode 100644 packages/eslint-codegen-model/examples/model-facades.ts delete mode 100644 packages/eslint-codegen-model/examples/workflow-facades.ts delete mode 100644 packages/eslint-codegen-model/index.js delete mode 100644 packages/eslint-codegen-model/package.json delete mode 100644 packages/eslint-codegen-model/src/cli.ts delete mode 100644 packages/eslint-codegen-model/src/index.ts delete mode 100644 packages/eslint-codegen-model/src/js-yaml.d.ts delete mode 100644 packages/eslint-codegen-model/src/normalise.ts delete mode 100644 packages/eslint-codegen-model/src/oxlint/index.ts delete mode 100644 packages/eslint-codegen-model/src/oxlint/rules/codegen.ts delete mode 100644 packages/eslint-codegen-model/src/presets/barrel.ts delete mode 100644 packages/eslint-codegen-model/src/presets/index.ts delete mode 100644 packages/eslint-codegen-model/src/presets/meta.ts delete mode 100644 packages/eslint-codegen-model/src/presets/model-facade.ts delete mode 100644 packages/eslint-codegen-model/src/presets/model.ts delete mode 100644 packages/eslint-codegen-model/src/shared/codegen-block.ts delete mode 100644 packages/eslint-codegen-model/src/shared/native-type-resolver.ts delete mode 100644 packages/eslint-codegen-model/src/shared/type-resolver.ts delete mode 100644 packages/eslint-codegen-model/tsconfig.json delete mode 100644 packages/eslint-shared-config/CHANGELOG.md delete mode 100644 packages/eslint-shared-config/package.json delete mode 100644 packages/eslint-shared-config/src/dprint.jsonc delete mode 100644 packages/eslint-shared-config/src/eslint.base.config.mjs delete mode 100644 packages/eslint-shared-config/src/eslint.vue.config.mjs delete mode 100644 packages/eslint-shared-config/src/oxlintrc.json delete mode 100644 packages/eslint-shared-config/src/plugin-effect-app.mjs delete mode 100644 packages/eslint-shared-config/test/no-await-effect.test.mts delete mode 100644 packages/eslint-shared-config/vitest.config.ts delete mode 100644 packages/infra/.madgerc delete mode 100644 packages/infra/.npmignore delete mode 100644 packages/infra/CHANGELOG.md delete mode 100644 packages/infra/_check.sh delete mode 100644 packages/infra/docs/cluster-storage.md delete mode 100644 packages/infra/docs/workflow-engine.md delete mode 100644 packages/infra/examples/query.ts delete mode 100644 packages/infra/package.json delete mode 100644 packages/infra/src/CUPS.ts delete mode 100644 packages/infra/src/ClusterCosmos.ts delete mode 100644 packages/infra/src/ClusterServiceBus.ts delete mode 100644 packages/infra/src/ContextProvider.ts delete mode 100644 packages/infra/src/Emailer/Sendgrid.ts delete mode 100644 packages/infra/src/Emailer/fake.ts delete mode 100644 packages/infra/src/MainFiberSet.ts delete mode 100644 packages/infra/src/QueueMaker/errors.ts delete mode 100644 packages/infra/src/SQL.ts delete mode 100644 packages/infra/src/SQL/Model.ts delete mode 100644 packages/infra/src/Store/Cosmos.ts delete mode 100644 packages/infra/src/Store/Cosmos/query.ts delete mode 100644 packages/infra/src/Store/Disk.ts delete mode 100644 packages/infra/src/Store/Memory.ts delete mode 100644 packages/infra/src/Store/SQL.ts delete mode 100644 packages/infra/src/Store/SQL/Pg.ts delete mode 100644 packages/infra/src/Store/SQL/query.ts delete mode 100644 packages/infra/src/Store/codeFilter.ts delete mode 100644 packages/infra/src/Store/index.test.ts.bak delete mode 100644 packages/infra/src/Store/index.ts delete mode 100644 packages/infra/src/Store/jsonDocument.ts delete mode 100644 packages/infra/src/Store/utils.ts delete mode 100644 packages/infra/src/WorkflowEngineCosmos.ts delete mode 100644 packages/infra/src/WorkflowEngineSqlite.ts delete mode 100644 packages/infra/src/arbs.ts delete mode 100644 packages/infra/src/codec.ts delete mode 100644 packages/infra/src/cosmos-client.ts delete mode 100644 packages/infra/src/errorReporter.ts delete mode 100644 packages/infra/src/errors.ts delete mode 100644 packages/infra/src/fileUtil.ts delete mode 100644 packages/infra/src/index.ts delete mode 100644 packages/infra/src/internal/RequestContextMiddleware.ts delete mode 100644 packages/infra/src/internal/auth.ts delete mode 100644 packages/infra/src/internal/events.ts delete mode 100644 packages/infra/src/internal/health.ts delete mode 100644 packages/infra/src/layerUtils.ts delete mode 100644 packages/infra/src/logger.ts delete mode 100644 packages/infra/src/logger/jsonLogger.ts delete mode 100644 packages/infra/src/logger/logFmtLogger.ts delete mode 100644 packages/infra/src/logger/shared.ts delete mode 100644 packages/infra/src/middlewares.ts delete mode 100644 packages/infra/src/mongo-client.ts delete mode 100644 packages/infra/src/otel.ts delete mode 100644 packages/infra/src/rateLimit.ts delete mode 100644 packages/infra/src/redis-client.ts delete mode 100644 packages/infra/src/reportError.ts delete mode 100644 packages/infra/src/routing.ts delete mode 100644 packages/infra/src/routing/middleware.ts delete mode 100644 packages/infra/src/routing/middleware/RouterMiddleware.ts delete mode 100644 packages/infra/src/routing/middleware/middleware.ts delete mode 100644 packages/infra/src/routing/schema/jwt.ts delete mode 100644 packages/infra/src/routing/tsort.ts delete mode 100644 packages/infra/src/routing/utils.ts delete mode 100644 packages/infra/src/test.ts delete mode 100644 packages/infra/src/util.ts delete mode 100644 packages/infra/src/vitest.ts delete mode 100644 packages/infra/test/arbs-magnitude.test.ts delete mode 100644 packages/infra/test/arbs.test.ts delete mode 100644 packages/infra/test/auth.test.ts delete mode 100644 packages/infra/test/cluster-cosmos.test.ts delete mode 100644 packages/infra/test/cluster-servicebus.test.ts delete mode 100644 packages/infra/test/cluster-sqlite.test.ts delete mode 100644 packages/infra/test/contextProvider.test.ts delete mode 100644 packages/infra/test/controller.test.ts delete mode 100644 packages/infra/test/cosmos-container-throughput.test.ts delete mode 100644 packages/infra/test/cosmos-query.test.ts delete mode 100644 packages/infra/test/fixtures.ts delete mode 100644 packages/infra/test/json-lower.test.ts delete mode 100644 packages/infra/test/layerUtils.test.ts delete mode 100644 packages/infra/test/query.test.ts delete mode 100644 packages/infra/test/rawQuery.test.ts delete mode 100644 packages/infra/test/repository-ext.test.ts delete mode 100644 packages/infra/test/repository-legacy-document.test.ts delete mode 100644 packages/infra/test/requires.test.ts delete mode 100644 packages/infra/test/router-generator.test.ts delete mode 100644 packages/infra/test/routing-interruptibility.test.ts delete mode 100644 packages/infra/test/rpc-context-map-streaming.test.ts delete mode 100644 packages/infra/test/rpc-e2e-invalidation.test.ts delete mode 100644 packages/infra/test/rpc-multi-middleware.test.ts delete mode 100644 packages/infra/test/rpc-stream-fullstack.test.ts delete mode 100644 packages/infra/test/sql-store.test.ts delete mode 100644 packages/infra/test/validateSample.test.ts delete mode 100644 packages/infra/test/workflow-engine-cosmos.test.ts delete mode 100644 packages/infra/test/workflow-engine-sqlite.test.ts delete mode 100644 packages/infra/tsconfig.examples.json delete mode 100644 packages/infra/tsconfig.json delete mode 100644 packages/infra/tsconfig.src.json delete mode 100644 packages/infra/tsconfig.test.json delete mode 100644 packages/infra/types/modules.d.ts delete mode 100644 packages/infra/vitest.config.ts rename {repos/effect/packages => packages}/opentelemetry/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/opentelemetry/LICENSE (100%) rename {repos/effect/packages => packages}/opentelemetry/README.md (100%) rename {repos/effect/packages => packages}/opentelemetry/package.json (99%) rename {repos/effect/packages => packages}/opentelemetry/src/NodeSdk.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelLogger.ts (88%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelMetrics.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelTracer.ts (89%) rename {repos/effect/packages => packages}/opentelemetry/src/Resource.ts (80%) rename {repos/effect/packages => packages}/opentelemetry/src/WebSdk.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/index.ts (96%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/attributes.ts (68%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/metrics.ts (97%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelLogger.test.ts (68%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelMetrics.test.ts (88%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelTracer.test.ts (66%) create mode 100644 packages/opentelemetry/test/Resource.test.ts rename {repos/effect/packages => packages}/opentelemetry/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/browser/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/platform/browser/LICENSE (100%) rename {repos/effect/packages => packages}/platform/browser/README.md (100%) rename {repos/effect/packages => packages}/platform/browser/package.json (98%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserCrypto.ts (97%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserHttpClient.ts (98%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserKeyValueStore.ts (98%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserPersistence.ts (99%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserRuntime.ts (97%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserSocket.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserStream.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserWorker.ts (97%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserWorkerRunner.ts (98%) rename {repos/effect/packages => packages}/platform/browser/src/Clipboard.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/Geolocation.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDb.ts (95%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbDatabase.ts (98%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbQueryBuilder.ts (99%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbTable.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbVersion.ts (96%) rename {repos/effect/packages => packages}/platform/browser/src/Permissions.ts (95%) rename {repos/effect/packages => packages}/platform/browser/src/index.ts (77%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserCrypto.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserKeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserPersistence.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserPersistencePersistedCache.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbDatabase.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbQueryBuilder.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbTable.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbVersion.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/Permissions.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/RpcWorker.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-worker.ts (100%) rename {repos/effect/packages => packages}/platform/browser/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/browser/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/platform/bun/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/platform/bun/LICENSE (100%) rename {repos/effect/packages => packages}/platform/bun/README.md (100%) rename {repos/effect/packages => packages}/platform/bun/package.json (98%) rename {repos/effect/packages => packages}/platform/bun/src/BunChildProcessSpawner.ts (86%) rename {repos/effect/packages => packages}/platform/bun/src/BunClusterHttp.ts (98%) rename {repos/effect/packages => packages}/platform/bun/src/BunClusterSocket.ts (98%) rename {repos/effect/packages => packages}/platform/bun/src/BunCrypto.ts (95%) rename {repos/effect/packages => packages}/platform/bun/src/BunFileSystem.ts (93%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpClient.ts (83%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpPlatform.ts (98%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpServer.ts (97%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpServerRequest.ts (93%) rename {repos/effect/packages => packages}/platform/bun/src/BunMultipart.ts (96%) rename {repos/effect/packages => packages}/platform/bun/src/BunPath.ts (92%) rename {repos/effect/packages => packages}/platform/bun/src/BunRedis.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunRuntime.ts (97%) rename {repos/effect/packages => packages}/platform/bun/src/BunServices.ts (96%) rename {repos/effect/packages => packages}/platform/bun/src/BunSink.ts (81%) rename {repos/effect/packages => packages}/platform/bun/src/BunSocket.ts (96%) rename {repos/effect/packages => packages}/platform/bun/src/BunSocketServer.ts (82%) rename {repos/effect/packages => packages}/platform/bun/src/BunStdio.ts (93%) rename {repos/effect/packages => packages}/platform/bun/src/BunStream.ts (98%) rename {repos/effect/packages => packages}/platform/bun/src/BunTerminal.ts (94%) rename {repos/effect/packages => packages}/platform/bun/src/BunWorker.ts (97%) rename {repos/effect/packages => packages}/platform/bun/src/BunWorkerRunner.ts (98%) rename {repos/effect/packages => packages}/platform/bun/src/index.ts (77%) rename {repos/effect/packages => packages}/platform/bun/test/BunClusterHttp.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunHttpServer.test.ts (82%) rename {repos/effect/packages => packages}/platform/bun/test/BunMultipart.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunStream.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/deno/CHANGELOG.md (91%) rename {repos/effect/packages => packages}/platform/deno/LICENSE (100%) rename {repos/effect/packages => packages}/platform/deno/README.md (100%) rename {repos/effect/packages => packages}/platform/deno/package.json (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoChildProcessSpawner.ts (99%) rename {repos/effect/packages => packages}/platform/deno/src/DenoClusterHttp.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/DenoClusterSocket.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/DenoCrypto.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoFileSystem.ts (99%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpClient.ts (83%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpPlatform.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpServer.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpServerRequest.ts (93%) rename {repos/effect/packages => packages}/platform/deno/src/DenoKeyValueStore.ts (91%) rename {repos/effect/packages => packages}/platform/deno/src/DenoMultipart.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoPath.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoRedis.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoRuntime.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoServices.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoSocket.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/DenoSocketServer.ts (82%) rename {repos/effect/packages => packages}/platform/deno/src/DenoStdio.ts (97%) rename {repos/effect/packages => packages}/platform/deno/src/DenoTerminal.ts (94%) rename {repos/effect/packages => packages}/platform/deno/src/DenoWorker.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoWorkerRunner.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/index.ts (78%) rename {repos/effect/packages => packages}/platform/deno/src/internal/error.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoChildProcessSpawner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoCrypto.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoFileSystem.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpServer.test.ts (94%) rename {repos/effect/packages => packages}/platform/deno/test/DenoKeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoPath.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoRedis.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoSocket.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoSocketServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoStdio.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoTerminal.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/RpcWorker.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/cluster/SocketRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/deno-terminal.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-worker.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/text.txt (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/ca.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/cert.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/key.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/internal/error.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/node-shared/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/platform/node-shared/LICENSE (100%) rename {repos/effect/packages => packages}/platform/node-shared/README.md (100%) rename {repos/effect/packages => packages}/platform/node-shared/benchmark/Socket.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/package.json (95%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeChildProcessSpawner.ts (99%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeClusterSocket.ts (97%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeCrypto.ts (96%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeFileSystem.ts (99%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeHttpCompression.ts (97%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodePath.ts (95%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeRuntime.ts (97%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSink.ts (98%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSocket.ts (98%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSocketServer.ts (98%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeStdio.ts (97%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeStream.ts (98%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeTerminal.ts (98%) rename {repos/effect/packages => packages}/platform/node-shared/src/index.ts (77%) rename {repos/effect/packages => packages}/platform/node-shared/src/internal/nodeChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/internal/utils.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/HttpPlatform.test-utils.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeChildProcessSpawner.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeFileSystem.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodePath.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeSink.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeStdio.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeStream.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeTerminal.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/helloworld.tar.gz (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/node-terminal.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/process-group.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/node/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/platform/node/LICENSE (100%) rename {repos/effect/packages => packages}/platform/node/README.md (100%) rename {repos/effect/packages => packages}/platform/node/package.json (90%) rename {repos/effect/packages => packages}/platform/node/src/NodeChildProcessSpawner.ts (86%) rename {repos/effect/packages => packages}/platform/node/src/NodeClusterHttp.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeClusterSocket.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeCrypto.ts (95%) rename {repos/effect/packages => packages}/platform/node/src/NodeFileSystem.ts (94%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpClient.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpIncomingMessage.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpPlatform.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpServer.ts (95%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpServerRequest.ts (94%) rename {repos/effect/packages => packages}/platform/node/src/NodeMultipart.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeMultipartParser.ts (96%) rename {repos/effect/packages => packages}/platform/node/src/NodePath.ts (92%) rename {repos/effect/packages => packages}/platform/node/src/NodeRedis.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeRuntime.ts (97%) rename {repos/effect/packages => packages}/platform/node/src/NodeServices.ts (96%) rename {repos/effect/packages => packages}/platform/node/src/NodeSink.ts (81%) rename {repos/effect/packages => packages}/platform/node/src/NodeSocket.ts (96%) rename {repos/effect/packages => packages}/platform/node/src/NodeSocketServer.ts (82%) rename {repos/effect/packages => packages}/platform/node/src/NodeStdio.ts (94%) rename {repos/effect/packages => packages}/platform/node/src/NodeStream.ts (82%) rename {repos/effect/packages => packages}/platform/node/src/NodeTerminal.ts (94%) rename {repos/effect/packages => packages}/platform/node/src/NodeWorker.ts (98%) rename {repos/effect/packages => packages}/platform/node/src/NodeWorkerRunner.ts (99%) rename {repos/effect/packages => packages}/platform/node/src/Undici.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/index.ts (78%) rename {repos/effect/packages => packages}/platform/node/test/HttpApi.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/HttpStaticServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/HttpStaticServerConditional.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/MultipartParser.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeCrypto.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpServer.test.ts (86%) rename {repos/effect/packages => packages}/platform/node/test/NodeRedis.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeSocket.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/OpenApi.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/RpcServer.test.ts (76%) rename {repos/effect/packages => packages}/platform/node/test/__snapshots__/HttpApi.test.ts.snap (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/ClusterCron.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Entity.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Locks.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Persistence.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/README.md (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Residency.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Smoke.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/StreamDisconnect.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Transport.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Workflow.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/globalSetup.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/harness.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/MessageStorageTest.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SocketRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SqlMessageStorage.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SqlRunnerStorage.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server-outside.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/conditional.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/custom/home.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/file.binx (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/guide/index.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/hello.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/index.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/range.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/mysql2-utils.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/pg-utils.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/text.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/tls/cert.pem (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/tls/key.pem (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/worker-runner.ts (100%) rename {repos/effect/packages => packages}/platform/node/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/clickhouse/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/sql/clickhouse/LICENSE (100%) rename {repos/effect/packages => packages}/sql/clickhouse/README.md (100%) rename {repos/effect/packages => packages}/sql/clickhouse/package.json (98%) rename {repos/effect/packages => packages}/sql/clickhouse/src/ClickhouseClient.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/src/ClickhouseMigrator.ts (95%) rename {repos/effect/packages => packages}/sql/clickhouse/src/index.ts (85%) rename {repos/effect/packages => packages}/sql/clickhouse/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/d1/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/d1/LICENSE (100%) rename {repos/effect/packages => packages}/sql/d1/README.md (100%) rename {repos/effect/packages => packages}/sql/d1/package.json (95%) rename {repos/effect/packages => packages}/sql/d1/src/D1Client.ts (95%) rename {repos/effect/packages => packages}/sql/d1/src/index.ts (88%) rename {repos/effect/packages => packages}/sql/d1/test/Client.test.ts (91%) rename {repos/effect/packages => packages}/sql/d1/test/Resolver.test.ts (100%) rename {repos/effect/packages => packages}/sql/d1/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/d1/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/libsql/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/libsql/LICENSE (100%) rename {repos/effect/packages => packages}/sql/libsql/README.md (100%) rename {repos/effect/packages => packages}/sql/libsql/package.json (96%) rename {repos/effect/packages => packages}/sql/libsql/src/LibsqlClient.ts (98%) rename {repos/effect/packages => packages}/sql/libsql/src/LibsqlMigrator.ts (95%) rename {repos/effect/packages => packages}/sql/libsql/src/index.ts (77%) rename {repos/effect/packages => packages}/sql/libsql/test/Client.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/test/Resolver.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/test/util.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/mssql/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/mssql/LICENSE (100%) rename {repos/effect/packages => packages}/sql/mssql/README.md (100%) rename {repos/effect/packages => packages}/sql/mssql/package.json (94%) rename {repos/effect/packages => packages}/sql/mssql/src/MssqlClient.ts (99%) rename {repos/effect/packages => packages}/sql/mssql/src/MssqlMigrator.ts (95%) rename {repos/effect/packages => packages}/sql/mssql/src/Parameter.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/src/Procedure.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/src/index.ts (84%) rename {repos/effect/packages => packages}/sql/mssql/test/Binary.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/Persistence.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/mysql2/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/mysql2/LICENSE (100%) rename {repos/effect/packages => packages}/sql/mysql2/README.md (100%) rename {repos/effect/packages => packages}/sql/mysql2/package.json (94%) rename {repos/effect/packages => packages}/sql/mysql2/src/MysqlClient.ts (98%) rename {repos/effect/packages => packages}/sql/mysql2/src/MysqlMigrator.ts (98%) rename {repos/effect/packages => packages}/sql/mysql2/src/index.ts (77%) rename {repos/effect/packages => packages}/sql/mysql2/test/Client.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/KeyValueStore.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/Model.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/MysqlClient.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/Persistence.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/SqlEventLogServerUnencrypted.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/pg/CHANGELOG.md (96%) rename {repos/effect/packages => packages}/sql/pg/LICENSE (100%) rename {repos/effect/packages => packages}/sql/pg/README.md (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/PgClient.ts (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/PgCodec.ts (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/README.md (100%) rename {repos/effect/packages => packages}/sql/pg/package.json (94%) rename {repos/effect/packages => packages}/sql/pg/src/PgAuth.ts (97%) rename {repos/effect/packages => packages}/sql/pg/src/PgClient.ts (97%) rename {repos/effect/packages => packages}/sql/pg/src/PgConnection.ts (99%) rename {repos/effect/packages => packages}/sql/pg/src/PgMigrator.ts (98%) rename {repos/effect/packages => packages}/sql/pg/src/PgPool.ts (98%) rename {repos/effect/packages => packages}/sql/pg/src/PgProtocol.ts (96%) rename {repos/effect/packages => packages}/sql/pg/src/PgTypes.ts (94%) rename {repos/effect/packages => packages}/sql/pg/src/index.ts (75%) rename {repos/effect/packages => packages}/sql/pg/src/internal/connection.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/internal/password.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/internal/sqlError.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/Client.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/KeyValueStore.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/Persistence.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgAuth.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.in-process.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.integration.test.ts (92%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgMigrator.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgPool.in-process.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgPool.integration.test.ts (82%) rename {repos/effect/packages => packages}/sql/pg/test/PgProtocol.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgTypes.test.ts (94%) rename {repos/effect/packages => packages}/sql/pg/test/SqlErrorClassification.test.ts (100%) create mode 100644 packages/sql/pg/test/SqlEventJournal.integration.test.ts create mode 100644 packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts rename {repos/effect/packages => packages}/sql/pg/test/StartupParameters.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/TransactionAcquire.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/fixtures/goldens.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/fixtures/regenerate.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/pg/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/pglite/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/pglite/LICENSE (100%) rename {repos/effect/packages => packages}/sql/pglite/README.md (100%) rename {repos/effect/packages => packages}/sql/pglite/package.json (98%) rename {repos/effect/packages => packages}/sql/pglite/src/PgliteClient.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/src/PgliteMigrator.ts (95%) rename {repos/effect/packages => packages}/sql/pglite/src/index.ts (84%) rename {repos/effect/packages => packages}/sql/pglite/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Json.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Migrator.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/PersistedQueue.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Transaction.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/sqlite-bun/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-bun/src/SqliteClient.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-bun/src/SqliteMigrator.ts (97%) rename {repos/effect/packages/sql/sqlite-do => packages/sql/sqlite-bun}/src/index.ts (84%) rename {repos/effect/packages => packages}/sql/sqlite-bun/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/sqlite-do/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/package.json (95%) rename {repos/effect/packages => packages}/sql/sqlite-do/src/SqliteClient.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/src/SqliteMigrator.ts (97%) rename {repos/effect/packages/sql/sqlite-node => packages/sql/sqlite-do}/src/index.ts (77%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/Miniflare.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/fixtures/transaction.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/CHANGELOG.md (97%) rename {repos/effect/packages => packages}/sql/sqlite-node/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/package.json (96%) rename {repos/effect/packages => packages}/sql/sqlite-node/src/SqliteClient.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-node/src/SqliteMigrator.ts (97%) rename {repos/effect/packages/sql/sqlite-react-native => packages/sql/sqlite-node}/src/index.ts (77%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Persistence.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Resolver.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventJournal.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventLogServerEncrypted.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventLogServerUnencrypted.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlModelServices.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqliteMigrator.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/src/SqliteClient.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/src/SqliteMigrator.ts (96%) rename {repos/effect/packages/sql/sqlite-bun => packages/sql/sqlite-react-native}/src/index.ts (77%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/OpfsWorker.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/SqliteClient.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/SqliteMigrator.ts (96%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/index.ts (76%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/internal/opfsWorker.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/sqlite-wasm.d.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/test/Memory.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/codegen.schema.json (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/package.json (97%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Config.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Discovery.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Generator.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/PostProcess.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/SpecFetcher.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/test/Generator.test.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/test/OpenRouterTranscriptions.test.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/package.json (97%) rename {repos/effect/packages => packages}/tools/ai-docgen/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/api-diff/README.md (100%) rename {repos/effect/packages => packages}/tools/api-diff/package.json (91%) rename {repos/effect/packages => packages}/tools/api-diff/src/Annotations.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/ApiDiff.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Cli.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Diff.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Discovery.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Error.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Json.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/MigrationDoc.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Model.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Report.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Snapshot.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Worktrees.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Annotations.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Cli.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Diff.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Discovery.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Error.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/MigrationDoc.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Report.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Snapshot.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Worktrees.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/utils.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/bundle/README.md (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/arbitrary-combinators.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/basic.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/batching.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/brand.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/cache.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/config.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/differ.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/http-client.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/http-router.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/logger.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/metric.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/optic.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/pubsub.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/queue.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schedule.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-bigdecimal.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-binary.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-class.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-fromJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-representation-roundtrip.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-string-transformation.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-string.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-template-literal.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toArbitrary.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toCodeDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toCodecJson.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toEquivalence.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toFormatter.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toRepresentation.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/stm.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/stream.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/package.json (96%) rename {repos/effect/packages => packages}/tools/bundle/src/Cli.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Fixtures.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Plugins.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Reporter.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Rollup.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/test/HttpRouterEsbuild.test.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/test/Plugins.test.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.fixtures.json (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.src.json (100%) rename {repos/effect/packages => packages}/tools/docgen/CHANGELOG.md (91%) rename {repos/effect/packages => packages}/tools/docgen/LICENSE (100%) rename {repos/effect/packages => packages}/tools/docgen/README.md (100%) rename {repos/effect/packages => packages}/tools/docgen/package.json (95%) rename {repos/effect/packages => packages}/tools/docgen/schema.json (100%) rename {repos/effect/packages => packages}/tools/docgen/src/CLI.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Checker.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Configuration.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Core.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Domain.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Parser.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Printer.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/index.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/internal/markdown-toc.d.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Checker.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Configuration.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Core.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Parser.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/fixtures/invalid-json.txt (100%) rename {repos/effect/packages => packages}/tools/docgen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/doctest/CHANGELOG.md (91%) rename {repos/effect/packages => packages}/tools/doctest/README.md (100%) rename {repos/effect/packages => packages}/tools/doctest/package.json (91%) rename {repos/effect/packages => packages}/tools/doctest/src/Plugin.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Protocol.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Runner.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Runtime.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Source.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Transform.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/index.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Plugin.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Protocol.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Runtime.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Source.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Transform.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/helper.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/typed.md (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/typed.mdx (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/src/typed.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/vitest.config.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/jsdocs/package.json (96%) rename {repos/effect/packages => packages}/tools/jsdocs/src/Jsdocs.ts (97%) rename {repos/effect/packages => packages}/tools/jsdocs/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/jsdocs/test/jsdocs.test.ts (79%) rename {repos/effect/packages => packages}/tools/jsdocs/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/tools/openapi-generator/LICENSE (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/README.md (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/package.json (97%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/HttpApiTransformer.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/JsonSchemaGenerator.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiGenerator.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiPatch.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiTransformer.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/ParsedOperation.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/Utils.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/JsonSchemaGenerator.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/JsonSchemaGeneratorRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiGenerator.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiGeneratorCli.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiPatch.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiTransformer.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/Utils.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/cli-basic-spec.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/cli-warning-spec.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/invalid-op.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/missing-path.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/missing-value.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-add.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-multiple.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-patch.yaml (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-remove.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-replace.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/oxc/oxlintrc.json (100%) rename {repos/effect/packages => packages}/tools/oxc/package.json (94%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/index.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-bigint-literals.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-import-from-barrel-package.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-js-extension-imports.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-opaque-instance-fields.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-unused-internal.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-import-from-barrel-package.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-js-extension-imports.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-opaque-instance-fields.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-unused-internal.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/utils.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/utils/package.json (97%) rename {repos/effect/packages => packages}/tools/utils/src/Codegen.ts (97%) rename {repos/effect/packages => packages}/tools/utils/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/utils/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/utils/src/commands/codegen.ts (100%) rename {repos/effect/packages => packages}/tools/utils/test/Codegen.test.ts (89%) rename {repos/effect/packages => packages}/tools/utils/tsconfig.json (100%) rename {repos/effect/packages => packages}/vitest/CHANGELOG.md (95%) rename {repos/effect/packages => packages}/vitest/LICENSE (100%) rename {repos/effect/packages => packages}/vitest/README.md (100%) rename {repos/effect/packages => packages}/vitest/package.json (94%) rename {repos/effect/packages => packages}/vitest/src/index.ts (84%) rename {repos/effect/packages => packages}/vitest/src/internal/internal.ts (100%) rename {repos/effect/packages => packages}/vitest/src/utils.ts (94%) rename {repos/effect/packages => packages}/vitest/test/arbitrary-configuration.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/fixtures.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/index.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/isolation.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/layer-concurrency.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/nested-isolation.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/timeout.test.ts (79%) rename {repos/effect/packages => packages}/vitest/tsconfig.json (100%) rename {repos/effect/packages => packages}/vitest/typetest/index.tst.ts (100%) delete mode 100644 packages/vue-components/.prettierrc delete mode 100644 packages/vue-components/.storybook/main.ts delete mode 100644 packages/vue-components/.storybook/preview.ts delete mode 100644 packages/vue-components/.storybook/tsconfig.json delete mode 100644 packages/vue-components/.vscode/extensions.json delete mode 100644 packages/vue-components/CHANGELOG.md delete mode 100644 packages/vue-components/README.md delete mode 100644 packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts delete mode 100644 packages/vue-components/__tests__/IntegerValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Defaults.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Meta.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionLegacyWarning.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionNested.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionRequired.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionRoot.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TransformationSchema.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TransformedStruct.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/UnregisteredFieldError.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/WithDefaultConstructorPersistency.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/createUseFormWithCustomInput.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaFormTest.vue delete mode 100644 packages/vue-components/__tests__/OmegaIntlProvider.vue delete mode 100644 packages/vue-components/__tests__/migration/schema-v4-migration.test.ts delete mode 100644 packages/vue-components/__tests__/setup.ts delete mode 100644 packages/vue-components/__tests__/test-array-meta.test.ts delete mode 100644 packages/vue-components/__tests__/test-nullor-duplicate.test.ts delete mode 100644 packages/vue-components/__tests__/test-union-meta.test.ts delete mode 100644 packages/vue-components/__tests__/tsconfig.json delete mode 100644 packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md delete mode 100644 packages/vue-components/docs/superpowers/specs/2026-04-27-omegaform-refactor-design.md delete mode 100644 packages/vue-components/env.d.ts delete mode 100644 packages/vue-components/eslint.config.mjs delete mode 100644 packages/vue-components/package.json delete mode 100644 packages/vue-components/src/assets/.keep delete mode 100644 packages/vue-components/src/components/CommandButton.vue delete mode 100644 packages/vue-components/src/components/Dialog.vue delete mode 100644 packages/vue-components/src/components/FixedNuxtErrorBoundary.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/InputProps.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaArray.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaAutoGen.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaFormInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInputVuetify.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInternalInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaTaggedUnion.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaTaggedUnionInternal.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaWrapper.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/blockDialog.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/createUseFormWithCustomInput.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/decimalSeparatorInput.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/errors.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/getOmegaStore.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/hocs.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/index.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/inputs.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/checks.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/createMeta.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/defaults.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/redacted.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/types.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/walker.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/onMountedWithCleanup.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/persistency.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/submit.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/types.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/useOmegaForm.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/useRegisterField.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/validation/localized.ts delete mode 100644 packages/vue-components/src/components/index.ts delete mode 100644 packages/vue-components/src/components/style.css delete mode 100644 packages/vue-components/src/index.ts delete mode 100644 packages/vue-components/src/reset.css delete mode 100644 packages/vue-components/src/utils/index.ts delete mode 100644 packages/vue-components/stories/Commands.stories.ts delete mode 100644 packages/vue-components/stories/Commands/One.vue delete mode 100644 packages/vue-components/stories/Commands/components.ts delete mode 100644 packages/vue-components/stories/Commands/helpers.ts delete mode 100644 packages/vue-components/stories/Commands/useEffectRouter.ts delete mode 100644 packages/vue-components/stories/Dialog.stories.ts delete mode 100644 packages/vue-components/stories/Dialog/AsyncBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/AsyncBlockingContent.vue delete mode 100644 packages/vue-components/stories/Dialog/SimpleBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/SimpleBlockingContent.vue delete mode 100644 packages/vue-components/stories/Dialog/UploadBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/UploadBlockingContent.vue delete mode 100644 packages/vue-components/stories/FixedNuxtErrorBoundary.stories.ts delete mode 100644 packages/vue-components/stories/FixedNuxtErrorBoundary/Demo.vue delete mode 100644 packages/vue-components/stories/OmegaForm.stories.ts delete mode 100644 packages/vue-components/stories/OmegaForm/Array.vue delete mode 100644 packages/vue-components/stories/OmegaForm/AutoGeneration.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Booleans.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Clearable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/ComplexForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomInputClassName.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomLabelSlot.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Date.vue delete mode 100644 packages/vue-components/stories/OmegaForm/DecodeTransformFallback.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Defaults.vue delete mode 100644 packages/vue-components/stories/OmegaForm/DialogBlockingExamples.vue delete mode 100644 packages/vue-components/stories/OmegaForm/EmailForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/EnterSubmitRepro.vue delete mode 100644 packages/vue-components/stories/OmegaForm/FormTaggedUnion.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IndexedTable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/InputRegistry.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IntegerValidationGerman.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IntersectionExample.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Meta.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Null.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Nullable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/NullableNestedStruct.vue delete mode 100644 packages/vue-components/stories/OmegaForm/NumberInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/OmitConstructorDefaults.vue delete mode 100644 packages/vue-components/stories/OmegaForm/OptionalKey.vue delete mode 100644 packages/vue-components/stories/OmegaForm/PersistencyForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/ProgrammaticallyHandleSubmitCheckErrors.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RatingInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Redacted.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RootLevelTaggedUnion.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RootLevelTaggedUnionLegacy.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SetErrorOnSubmit.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SimpleForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SimpleFormVuetifyDefault.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SumExample.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Tanstack.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Union.vue delete mode 100644 packages/vue-components/stories/OmegaForm/UsingOmegaForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/WindowExitPrevention.vue delete mode 100644 packages/vue-components/stories/OmegaForm/WithDefaultConstructor.vue delete mode 100644 packages/vue-components/stories/OmegaForm/createUseFormWIthCustomInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/form.Input.vue delete mode 100644 packages/vue-components/stories/README.md delete mode 100644 packages/vue-components/stories/tsconfig.json delete mode 100644 packages/vue-components/tsconfig.build.json delete mode 100644 packages/vue-components/tsconfig.json delete mode 100644 packages/vue-components/types/vuetify-shims.d.ts delete mode 100644 packages/vue-components/vite.config.mts delete mode 100644 packages/vue-components/vitest.config.mts delete mode 100644 packages/vue/.madgerc delete mode 100644 packages/vue/.npmignore delete mode 100644 packages/vue/.prettierignore delete mode 100644 packages/vue/CHANGELOG.md delete mode 100644 packages/vue/docs/atom-query-api-redesign.md delete mode 100644 packages/vue/docs/mutation-command-atoms.md delete mode 100644 packages/vue/docs/query-key-invalidation.md delete mode 100644 packages/vue/examples/streamMutation.ts delete mode 100644 packages/vue/package.json delete mode 100644 packages/vue/src/atomQuery.ts delete mode 100644 packages/vue/src/commander.ts delete mode 100644 packages/vue/src/confirm.ts delete mode 100644 packages/vue/src/dependencyMetadata.ts delete mode 100644 packages/vue/src/errorReporter.ts delete mode 100644 packages/vue/src/form.ts delete mode 100644 packages/vue/src/index.ts delete mode 100644 packages/vue/src/internal/tanstackQuery.ts delete mode 100644 packages/vue/src/intl.ts delete mode 100644 packages/vue/src/lib.ts delete mode 100644 packages/vue/src/liveQueryInvalidation.ts delete mode 100644 packages/vue/src/makeClient.ts delete mode 100644 packages/vue/src/makeContext.ts delete mode 100644 packages/vue/src/makeIntl.ts delete mode 100644 packages/vue/src/makeUseCommand.ts delete mode 100644 packages/vue/src/mutate.ts delete mode 100644 packages/vue/src/query.ts delete mode 100644 packages/vue/src/queryLifetime.ts delete mode 100644 packages/vue/src/routeParams.ts delete mode 100644 packages/vue/src/runtime.ts delete mode 100644 packages/vue/src/suspense.ts delete mode 100644 packages/vue/src/toast.ts delete mode 100644 packages/vue/src/withToast.ts delete mode 100644 packages/vue/test/Mutation.test.ts delete mode 100644 packages/vue/test/dependencyInvalidation.test.ts delete mode 100644 packages/vue/test/form-validation-errors.test.ts delete mode 100644 packages/vue/test/form.test.ts delete mode 100644 packages/vue/test/inactiveInvalidation.test.ts delete mode 100644 packages/vue/test/interrupt-refetch-repro.test.ts delete mode 100644 packages/vue/test/lib.test.ts delete mode 100644 packages/vue/test/liveQueryInvalidation.test.ts delete mode 100644 packages/vue/test/makeClient.test.ts delete mode 100644 packages/vue/test/query-span.test.ts delete mode 100644 packages/vue/test/queryOptions.test.ts delete mode 100644 packages/vue/test/streamFinal.test.ts delete mode 100644 packages/vue/test/streamFn.test.ts delete mode 100644 packages/vue/test/stubs.ts delete mode 100644 packages/vue/test/suspense-regression.test.ts delete mode 100644 packages/vue/test/suspense.test.ts delete mode 100644 packages/vue/tsconfig.examples.json delete mode 100644 packages/vue/tsconfig.json delete mode 100644 packages/vue/tsconfig.src.json delete mode 100644 packages/vue/tsconfig.test.json delete mode 100644 packages/vue/vitest.config.ts delete mode 100644 patches/.gitkeep delete mode 100644 patches/@ben_12__eslint-plugin-dprint.patch delete mode 100644 patches/@changesets__cli@2.31.0.patch rename {repos/effect/patches => patches}/@changesets__get-github-info@1.0.1.patch (100%) delete mode 100644 patches/@sendgrid__mail.patch delete mode 100644 patches/@tanstack__query-core.patch delete mode 100644 patches/ts-plugin-sort-import-suggestions.patch delete mode 100644 patches/typescript.patch delete mode 100644 repos/effect/.changeset/config.json delete mode 100644 repos/effect/.envrc delete mode 100644 repos/effect/.gitignore delete mode 100644 repos/effect/.oxlintrc.json delete mode 100644 repos/effect/.vscode/extensions.json delete mode 100644 repos/effect/.vscode/settings.json delete mode 100644 repos/effect/README.md delete mode 100644 repos/effect/flake.lock delete mode 100644 repos/effect/flake.nix delete mode 100644 repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml delete mode 100644 repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml delete mode 100644 repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml delete mode 100644 repos/effect/package.json delete mode 100644 repos/effect/packages/ai/cloudflare/CHANGELOG.md delete mode 100644 repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts delete mode 100644 repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts delete mode 100644 repos/effect/packages/effect/test/httpapi/HttpApiError.test.ts delete mode 100644 repos/effect/packages/effect/test/observability/OtlpSerialization.test.ts delete mode 100644 repos/effect/packages/effect/typetest/reactivity/Atom.tst.ts delete mode 100644 repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts delete mode 100644 repos/effect/pnpm-lock.yaml delete mode 100644 repos/effect/pnpm-workspace.yaml delete mode 100644 repos/effect/tsconfig.base.json delete mode 100644 repos/effect/vitest.config.ts delete mode 100644 repos/effect/vitest.setup.ts rename {repos/effect/scratchpad => scratchpad}/index.ts (100%) rename {repos/effect/scratchpad => scratchpad}/package.json (100%) rename {repos/effect/scratchpad => scratchpad}/tsconfig.json (100%) delete mode 100644 scripts/agent-gh.mjs delete mode 100644 scripts/agent-pr-ready.mjs delete mode 100644 scripts/agent-pre-push.mjs rename {repos/effect/scripts => scripts}/bundle-analyze.sh (100%) rename {repos/effect/scripts => scripts}/bundle-compare-selected.sh (100%) rename {repos/effect/scripts => scripts}/bundle-compare.sh (100%) rename {repos/effect/scripts => scripts}/check-dist-types.mjs (100%) rename {repos/effect/scripts => scripts}/circular.mjs (100%) delete mode 100644 scripts/clean-dist.sh rename {repos/effect/scripts => scripts}/clean.mjs (100%) rename {repos/effect/scripts => scripts}/codemod.mjs (100%) rename {repos/effect/scripts => scripts}/codemods/jsdoc.ts (100%) delete mode 100644 scripts/convert_patch.js rename {repos/effect/scripts => scripts}/copy-ai-docs.mjs (100%) create mode 100644 scripts/effect-bot.mjs delete mode 100644 scripts/extract.sh delete mode 100755 scripts/hooks/post-checkout.test.sh delete mode 100644 scripts/install-git-hooks.mjs delete mode 100644 scripts/lib/agent-env.mjs delete mode 100644 scripts/lib/agent-gh-policy.mjs delete mode 100644 scripts/lib/agent-pr-state.mjs delete mode 100644 scripts/lib/agent-ship-gate-cache.mjs delete mode 100644 scripts/mergeTsConfig.mjs rename {repos/effect/scripts => scripts}/package-scalar.mjs (100%) rename {repos/effect/scripts => scripts}/package-swagger.mjs (100%) rename {repos/effect/scripts => scripts}/package.json (100%) delete mode 100644 scripts/rewrite-dist-declaration-imports.mjs rename {repos/effect/scripts => scripts}/set-strip-internal.mjs (100%) rename {repos/effect/scripts => scripts}/setup-agents.mjs (100%) delete mode 100644 scripts/sort.js delete mode 100644 scripts/stagePublishPackage.mjs delete mode 100644 scripts/sync-effect-subtree.js rename {repos/effect/scripts => scripts}/tsconfig.json (100%) create mode 100644 scripts/version.mjs rename {repos/effect/scripts => scripts}/worktree-setup.sh (100%) delete mode 100644 t3.json delete mode 100644 tsconfig.all.json rename repos/effect/tsconfig.json => tsconfig.json (100%) delete mode 100644 tsconfig.lib.json rename repos/effect/tsconfig.packages.json => tsconfig.packages.json (100%) delete mode 100644 tsconfig.plugins.json rename repos/effect/tsconfig.tests.json => tsconfig.tests.json (98%) rename repos/effect/tstyche.json => tstyche.json (100%) delete mode 100644 vite.config.base.ts rename repos/effect/vitest.docs.ts => vitest.docs.ts (100%) delete mode 100644 wiki/Home.md delete mode 100644 wiki/ResourceClients.md delete mode 100644 wiki/bag-of-tricks.md delete mode 100644 "wiki/effect\342\200\220app\342\200\220cli.md" delete mode 100644 wiki/error-reporter-migration.md delete mode 100644 wiki/repository-derived-query-invalidation.md diff --git a/repos/effect/.agents/AGENTS.md b/.agents/AGENTS.md similarity index 100% rename from repos/effect/.agents/AGENTS.md rename to .agents/AGENTS.md diff --git a/repos/effect/.agents/skills/ai-docs/SKILL.md b/.agents/skills/ai-docs/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/ai-docs/SKILL.md rename to .agents/skills/ai-docs/SKILL.md diff --git a/repos/effect/.agents/skills/bundle-analysis/SKILL.md b/.agents/skills/bundle-analysis/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/bundle-analysis/SKILL.md rename to .agents/skills/bundle-analysis/SKILL.md diff --git a/repos/effect/.agents/skills/changesets/SKILL.md b/.agents/skills/changesets/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/changesets/SKILL.md rename to .agents/skills/changesets/SKILL.md diff --git a/repos/effect/.agents/skills/changesets/authoring.md b/.agents/skills/changesets/authoring.md similarity index 97% rename from repos/effect/.agents/skills/changesets/authoring.md rename to .agents/skills/changesets/authoring.md index e1a1c22fe..40d4ef924 100644 --- a/repos/effect/.agents/skills/changesets/authoring.md +++ b/.agents/skills/changesets/authoring.md @@ -29,8 +29,8 @@ Choose the bump from current release policy: - Use `patch` for compatible fixes, `minor` for compatible additions, and `major` for breaks. - A break confined to APIs tagged `@stability unstable` is `minor`. A break - confined to APIs tagged `@stability experimental` is `patch`. APIs without a - stability tag follow strict semver. + confined to APIs tagged `@stability experimental` is `patch`. APIs tagged + `@stability stable` follow strict semver. - Use `major` only when a maintainer has approved a major release. - The release queue retargets PRs with `minor` changesets to `v4/next-minor` and PRs with `major` changesets to `v4/next-major`. Choose the level the diff --git a/repos/effect/.agents/skills/ci-maintenance/SKILL.md b/.agents/skills/ci-maintenance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/ci-maintenance/SKILL.md rename to .agents/skills/ci-maintenance/SKILL.md diff --git a/repos/effect/.agents/skills/ci-maintenance/privileged-workflows.md b/.agents/skills/ci-maintenance/privileged-workflows.md similarity index 100% rename from repos/effect/.agents/skills/ci-maintenance/privileged-workflows.md rename to .agents/skills/ci-maintenance/privileged-workflows.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/SKILL.md b/.agents/skills/dependency-maintenance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/SKILL.md rename to .agents/skills/dependency-maintenance/SKILL.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/coordinated-upgrades.md b/.agents/skills/dependency-maintenance/coordinated-upgrades.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/coordinated-upgrades.md rename to .agents/skills/dependency-maintenance/coordinated-upgrades.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/manifest-roles.md b/.agents/skills/dependency-maintenance/manifest-roles.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/manifest-roles.md rename to .agents/skills/dependency-maintenance/manifest-roles.md diff --git a/repos/effect/.agents/skills/effect-development/SKILL.md b/.agents/skills/effect-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/effect-development/SKILL.md rename to .agents/skills/effect-development/SKILL.md diff --git a/repos/effect/.agents/skills/jsdocs/SKILL.md b/.agents/skills/jsdocs/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/SKILL.md rename to .agents/skills/jsdocs/SKILL.md diff --git a/repos/effect/.agents/skills/jsdocs/categories.md b/.agents/skills/jsdocs/categories.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/categories.md rename to .agents/skills/jsdocs/categories.md diff --git a/repos/effect/.agents/skills/jsdocs/declarations.md b/.agents/skills/jsdocs/declarations.md similarity index 86% rename from repos/effect/.agents/skills/jsdocs/declarations.md rename to .agents/skills/jsdocs/declarations.md index 9795b4c9a..770d4ef07 100644 --- a/repos/effect/.agents/skills/jsdocs/declarations.md +++ b/.agents/skills/jsdocs/declarations.md @@ -53,7 +53,7 @@ Declaration tags appear in this order: 1. `@deprecated` 2. `@default` 3. `@see` -4. `@stability unstable` or `@stability experimental` (when applicable) +4. `@stability` 5. `@category` 6. `@since` @@ -65,9 +65,10 @@ Declaration tags appear in this order: non-empty `@default`, rejects `@category`, and follows the prose contract. - Any declaration permits one non-empty `@deprecated` and repeated non-empty `@see` tags. -- Use `@stability unstable` when an API may receive breaking changes in minor - releases, or `@stability experimental` when it may receive breaking changes - across patch versions. Leave the tag out for APIs that follow strict semver. +- Roots, export specifiers, and root namespaces require `@stability stable`, + `@stability unstable`, or `@stability experimental`. Use `unstable` when an + API may receive breaking changes in minor releases, `experimental` when it may + receive breaking changes across patch versions, and `stable` otherwise. - Tag any API that exposes a third-party dependency `@stability unstable`: an accessor to the underlying client or instance, options typed as the dependency's options, constructors that accept its instances, re-exports of @@ -84,9 +85,9 @@ fences. When present, the first top-level JSDoc is the module block unless TypeScript attaches it to a non-import first declaration. An `@internal` module is omitted. Module prose does not use the declaration template. Its tags are optional -non-empty `@deprecated`, repeated non-empty `@see`, optional `@stability unstable` -or `@stability experimental`, then required stable-semver `@since`. Its examples -and links follow the declaration contracts. +non-empty `@deprecated`, repeated non-empty `@see`, required `@stability`, then +required stable-semver `@since`. Its examples and links follow the declaration +contracts. Inline `{@link Symbol}` targets must resolve to TypeScript symbols; use normal Markdown links for URLs. Prefer code formatting when navigation does not help a diff --git a/repos/effect/.agents/skills/jsdocs/examples.md b/.agents/skills/jsdocs/examples.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/examples.md rename to .agents/skills/jsdocs/examples.md diff --git a/repos/effect/.agents/skills/migration-guidance/SKILL.md b/.agents/skills/migration-guidance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/SKILL.md rename to .agents/skills/migration-guidance/SKILL.md diff --git a/repos/effect/.agents/skills/migration-guidance/annotations.md b/.agents/skills/migration-guidance/annotations.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/annotations.md rename to .agents/skills/migration-guidance/annotations.md diff --git a/repos/effect/.agents/skills/migration-guidance/generation.md b/.agents/skills/migration-guidance/generation.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/generation.md rename to .agents/skills/migration-guidance/generation.md diff --git a/repos/effect/.agents/skills/package-development/SKILL.md b/.agents/skills/package-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/package-development/SKILL.md rename to .agents/skills/package-development/SKILL.md diff --git a/repos/effect/.agents/skills/package-development/dependencies.md b/.agents/skills/package-development/dependencies.md similarity index 100% rename from repos/effect/.agents/skills/package-development/dependencies.md rename to .agents/skills/package-development/dependencies.md diff --git a/repos/effect/.agents/skills/package-development/publishing.md b/.agents/skills/package-development/publishing.md similarity index 100% rename from repos/effect/.agents/skills/package-development/publishing.md rename to .agents/skills/package-development/publishing.md diff --git a/repos/effect/.agents/skills/package-development/registration.md b/.agents/skills/package-development/registration.md similarity index 100% rename from repos/effect/.agents/skills/package-development/registration.md rename to .agents/skills/package-development/registration.md diff --git a/repos/effect/.agents/skills/performance-analysis/SKILL.md b/.agents/skills/performance-analysis/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/SKILL.md rename to .agents/skills/performance-analysis/SKILL.md diff --git a/repos/effect/.agents/skills/performance-analysis/runtime.md b/.agents/skills/performance-analysis/runtime.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/runtime.md rename to .agents/skills/performance-analysis/runtime.md diff --git a/repos/effect/.agents/skills/performance-analysis/types.md b/.agents/skills/performance-analysis/types.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/types.md rename to .agents/skills/performance-analysis/types.md diff --git a/repos/effect/.agents/skills/test-development/SKILL.md b/.agents/skills/test-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/test-development/SKILL.md rename to .agents/skills/test-development/SKILL.md diff --git a/repos/effect/.agents/skills/test-development/displayed-types.md b/.agents/skills/test-development/displayed-types.md similarity index 100% rename from repos/effect/.agents/skills/test-development/displayed-types.md rename to .agents/skills/test-development/displayed-types.md diff --git a/repos/effect/.agents/skills/test-development/runtime.md b/.agents/skills/test-development/runtime.md similarity index 100% rename from repos/effect/.agents/skills/test-development/runtime.md rename to .agents/skills/test-development/runtime.md diff --git a/repos/effect/.agents/skills/test-development/types.md b/.agents/skills/test-development/types.md similarity index 100% rename from repos/effect/.agents/skills/test-development/types.md rename to .agents/skills/test-development/types.md diff --git a/repos/effect/.agents/skills/vendored-assets/SKILL.md b/.agents/skills/vendored-assets/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/vendored-assets/SKILL.md rename to .agents/skills/vendored-assets/SKILL.md diff --git a/repos/effect/.agents/skills/vendored-assets/browser-assets.md b/.agents/skills/vendored-assets/browser-assets.md similarity index 100% rename from repos/effect/.agents/skills/vendored-assets/browser-assets.md rename to .agents/skills/vendored-assets/browser-assets.md diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/active-query-invalidation.md b/.changeset/active-query-invalidation.md deleted file mode 100644 index de7f548f0..000000000 --- a/.changeset/active-query-invalidation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Invalidate only mounted queries (TanStack `refetchType: "active"`). Idle `gcTime` entries are marked stale and refetch on remount. `invalidateAndAwait` still waits for observed refetches to finish. diff --git a/.changeset/add-oxlint-integration.md b/.changeset/add-oxlint-integration.md deleted file mode 100644 index 3fba7c31d..000000000 --- a/.changeset/add-oxlint-integration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": minor ---- - -Add oxlint integration: run oxlint before ESLint, with `eslint-plugin-oxlint` disabling ESLint rules already covered by oxlint. Add repo-root `.oxlintrc.json` with shared rule config. Update `lint`/`lint-fix` scripts in cli, effect-app, and infra packages. diff --git a/.changeset/add-stream-mutation.md b/.changeset/add-stream-mutation.md deleted file mode 100644 index f3308a661..000000000 --- a/.changeset/add-stream-mutation.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@effect-app/vue": minor -"@effect-app/vue-components": minor ---- - -Add stream mutation support throughout the Vue commander pipeline. - -- `asStreamResult` utility (mirrors `asResult`, accepts `Stream` or a factory). Reactive ref updates with each emitted value (`waiting: true`) and finalises once the stream ends (`waiting: false`); errors surface as `AsyncResult.failure`. -- `clientFor` now exposes `mutateStream` for stream-type requests as a factory `(options?) => [resultRef, execute] & { id, running?, progress? }`. Always invoke `()` (optionally with `{ progress }`) to obtain a fresh ref+execute pair — independent invocations don't share state. Helpers expose the same shape under `xxxStream`. -- `Command.wrapStream(client.x)` and `Command.wrap(client.x)` build a CommanderWrap from a stream entry; the factory is called per command build. -- `Command.fn(client.x.mutateStream)` and `Command.fn(client.x.mutateStream({ progress }))` accept a stream factory or already-called tuple-with-id; the resulting command exposes `running` (live `AsyncResult`) and `progress` (formatted loading info) only when the factory was called with a `progress: (result) => Progress | undefined` formatter, where `Progress = string | { text: string; percentage: number }`. -- `CommandBase` adds `progress?: Progress` so the `CommandButton` component overrides the Vuetify `loader` slot with a `v-progress-circular` (using `model-value` for percentage when available, otherwise indeterminate) and the formatted text alongside. -- New example `examples/streamMutation.ts` shows modelling a long-running export operation that streams `OperationProgress | ExportComplete` events. diff --git a/.changeset/adopt-module-system.md b/.changeset/adopt-module-system.md deleted file mode 100644 index b4ffc101d..000000000 --- a/.changeset/adopt-module-system.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Adopt module system from effect-smol: replace barrel imports with specific submodule imports (`import * as X from "effect-app/X"` / `import * as X from "effect/X"`). diff --git a/.changeset/align-invalidation-entry-types.md b/.changeset/align-invalidation-entry-types.md deleted file mode 100644 index ce3049977..000000000 --- a/.changeset/align-invalidation-entry-types.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/vue": patch -"effect-app": patch ---- - -Align `InvalidationEntry` (vue) with `InvalidateQueryInstruction` (effect-app). - -`InvalidateQueryInstruction` is now parametrized over `Filters` / `Options` -(defaulting to `Record` so the core stays framework-agnostic). -`@effect-app/vue` exposes `InvalidationEntry` as a narrowed alias substituting -`@tanstack/vue-query`'s `InvalidateQueryFilters` and `InvalidateOptions`. Single -source of truth for the union shape across both packages. diff --git a/.changeset/align-schema-void-ts-semantics.md b/.changeset/align-schema-void-ts-semantics.md deleted file mode 100644 index 7bb4a8d9a..000000000 --- a/.changeset/align-schema-void-ts-semantics.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": patch ---- - -Align `Schema.Void` with TypeScript `void` return-value semantics (effect-smol PR #2475 / `b7d46ab`). - -`S.Void` now accepts **any present value** at runtime and discards it to `undefined`, while keeping the decoded/encoded type as `void` — matching a `void` return whose result callers never observe. This is implemented as an override of the AST node in a new `effect-app/SchemaAST` module (`export * from "effect/SchemaAST"` plus a `Void` subclass whose parser mirrors the PR's `fromAnyToConst(undefined)`), so there is a single canonical `Void` used everywhere, including RPC success schemas. - -- New `effect-app/SchemaAST` module; internal `SchemaAST` imports across the libs now route through it. -- Removed `ForceVoid` from `effect-app/client/makeClient` — use `S.Void` directly, which now carries this behaviour. -- `S.Void_` remains available as effect's original (`undefined`-only) Void. diff --git a/.changeset/all-phones-dream.md b/.changeset/all-phones-dream.md deleted file mode 100644 index 3dcb4dfbe..000000000 --- a/.changeset/all-phones-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -temp revert diff --git a/.changeset/amber-garlics-sing.md b/.changeset/amber-garlics-sing.md deleted file mode 100644 index 56e60c1c0..000000000 --- a/.changeset/amber-garlics-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Allow Struct and TaggedStruct make helpers to omit input when every constructor field is optional or defaulted, and preserve widening copy typings through a lighter named public type to improve TypeScript editor responsiveness. diff --git a/.changeset/api-client-isolate-layers.md b/.changeset/api-client-isolate-layers.md deleted file mode 100644 index 931a59b07..000000000 --- a/.changeset/api-client-isolate-layers.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"effect-app": patch ---- - -Isolate per-call `Effect.provide(layers)` / `Stream.provide(layers)` in the -RPC api client factory via `{ local: true }`. - -`layers` here is built per RPC invocation from a caller-supplied -`requestLevelLayers` plus a fresh `RequestName` layer. Today both pieces are -stateless, so no observable bug — but `Effect.provide(layer)` without -`local: true` resolves its `MemoMap` from the ambient fiber context, and on a -long-lived runtime (browser app, server fiber) any stateful layer slipped into -`requestLevelLayers` by a caller would be memoized and shared across every -subsequent RPC call. `{ local: true }` builds the layer fresh per call and -skips the ambient MemoMap entirely. - -Also documents the rule in `AGENTS.md` ("Per-request `Effect.provide(layer)` -must isolate its MemoMap") so the pattern is enforceable in code review and -caught early. Companion to the `provideOnRequestScope` MemoMap fix in -`@effect-app/infra`. diff --git a/.changeset/atom-query-vue.md b/.changeset/atom-query-vue.md deleted file mode 100644 index c13ba4fb6..000000000 --- a/.changeset/atom-query-vue.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Add Atom-native Vue query APIs, stream query pull atoms, and a TanStack-backed legacy query engine toggle. diff --git a/.changeset/batch-cosmos-acks.md b/.changeset/batch-cosmos-acks.md deleted file mode 100644 index 2c5d49414..000000000 --- a/.changeset/batch-cosmos-acks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Batch Cosmos ack message processing updates. diff --git a/.changeset/batch-dual-options.md b/.changeset/batch-dual-options.md deleted file mode 100644 index b84b454a8..000000000 --- a/.changeset/batch-dual-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Merge `batchPar` into `batch`, expose `concurrency` via an optional `BatchOptions` argument, and make `batch` dual (data-first + data-last). diff --git a/.changeset/big-teams-wash.md b/.changeset/big-teams-wash.md deleted file mode 100644 index 23c182f5a..000000000 --- a/.changeset/big-teams-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix bs span diff --git a/.changeset/blue-rats-admire.md b/.changeset/blue-rats-admire.md deleted file mode 100644 index bc8acc93c..000000000 --- a/.changeset/blue-rats-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Adjust Struct and TaggedStruct copy typing to follow class-style widening constraints while keeping structural copy runtime behavior. diff --git a/.changeset/bold-berries-change.md b/.changeset/bold-berries-change.md deleted file mode 100644 index 4b641c68b..000000000 --- a/.changeset/bold-berries-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fixes optionalKey input in OmegaForm diff --git a/.changeset/brave-animals-like.md b/.changeset/brave-animals-like.md deleted file mode 100644 index 7c366956a..000000000 --- a/.changeset/brave-animals-like.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -improve: support .ts extension diff --git a/.changeset/brave-bushes-fold.md b/.changeset/brave-bushes-fold.md deleted file mode 100644 index 195bb03e3..000000000 --- a/.changeset/brave-bushes-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix lock diff --git a/.changeset/brave-laws-add.md b/.changeset/brave-laws-add.md deleted file mode 100644 index 5fbc227ac..000000000 --- a/.changeset/brave-laws-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -remove hopefully obsolete Struct overrides diff --git a/.changeset/brave-oranges-try.md b/.changeset/brave-oranges-try.md deleted file mode 100644 index b01730ad4..000000000 --- a/.changeset/brave-oranges-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -enforce local diff --git a/.changeset/brave-years-fry.md b/.changeset/brave-years-fry.md deleted file mode 100644 index 99465728d..000000000 --- a/.changeset/brave-years-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -align CauseException diff --git a/.changeset/bright-camels-grin.md b/.changeset/bright-camels-grin.md deleted file mode 100644 index e47d4a412..000000000 --- a/.changeset/bright-camels-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Preserve field-level schema decode errors for relaxed Class and TaggedClass declarations so decode failures report nested constraints (for example min-length violations) instead of only a generic class-type mismatch. diff --git a/.changeset/bright-lions-share.md b/.changeset/bright-lions-share.md deleted file mode 100644 index b15f93094..000000000 --- a/.changeset/bright-lions-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Extend `Schema.Opaque` in `effect-app/Schema` to support an optional encoded-type generic while preserving the original single-generic behavior. diff --git a/.changeset/bright-mugs-paint.md b/.changeset/bright-mugs-paint.md deleted file mode 100644 index e550ba261..000000000 --- a/.changeset/bright-mugs-paint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Update `Effect.allLower` to call `svc.asEffect()` when available, ensuring service entries are normalized before `Effect.all` evaluation. diff --git a/.changeset/bright-peaches-wave.md b/.changeset/bright-peaches-wave.md deleted file mode 100644 index 3ea55befc..000000000 --- a/.changeset/bright-peaches-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Preserve omitted-input make helpers through Schema.Opaque when the wrapped schema allows optional or default-only constructor input. diff --git a/.changeset/bright-trees-count.md b/.changeset/bright-trees-count.md deleted file mode 100644 index f43f86474..000000000 --- a/.changeset/bright-trees-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix config nested diff --git a/.changeset/builtin-map-union-receiver.md b/.changeset/builtin-map-union-receiver.md deleted file mode 100644 index a76007f5e..000000000 --- a/.changeset/builtin-map-union-receiver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Fix the global `Array`/`ReadonlyArray.map` override breaking union-of-array receivers. The previous `this: NonEmptyArray`/`NonEmptyReadonlyArray` overload was selected-then-rejected on a union receiver (e.g. `(readonly A[] | readonly B[]).map(...)`), raising TS2684. Replaced with a single conditional-return signature (`this extends NonEmpty… ? NonEmpty : U[]`) that preserves NonEmpty refinement without a `this` parameter, so union-array `.map` calls type-check again. diff --git a/.changeset/cached-per-request-helper.md b/.changeset/cached-per-request-helper.md deleted file mode 100644 index 8964ac2a0..000000000 --- a/.changeset/cached-per-request-helper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add `cachedPerRequest` helper to `ContextMapContainer`. Runs a given Effect at most once per ContextMap (i.e. per request) and stores the result in the ContextMap under a fresh symbol, using the ContextMap's shared semaphore for safe single initialization. Use as a building block for any per-request memoized value (request resolver caches, per-request `Cache.make` instances, etc.). diff --git a/.changeset/calm-clocks-observe.md b/.changeset/calm-clocks-observe.md deleted file mode 100644 index 3fb003aa7..000000000 --- a/.changeset/calm-clocks-observe.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Replace repository codec child spans with parent-span timing attributes and bounded metrics, and record database operation timing for instrumented stores. diff --git a/.changeset/calm-cosmos-cluster.md b/.changeset/calm-cosmos-cluster.md deleted file mode 100644 index 6ca46d941..000000000 --- a/.changeset/calm-cosmos-cluster.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add Cosmos DB backed storage layers for Effect Cluster message and runner state. diff --git a/.changeset/calm-filter-cycle.md b/.changeset/calm-filter-cycle.md deleted file mode 100644 index 01cd97d0b..000000000 --- a/.changeset/calm-filter-cycle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Break the circular dependency between the memory store and code filter modules. diff --git a/.changeset/calm-pallets-listen.md b/.changeset/calm-pallets-listen.md deleted file mode 100644 index d015de043..000000000 --- a/.changeset/calm-pallets-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Derive scoped dependency reads and writes from typed query filters and annotated model relationships, including previous aliases. diff --git a/.changeset/calm-pets-float.md b/.changeset/calm-pets-float.md deleted file mode 100644 index c9bea369a..000000000 --- a/.changeset/calm-pets-float.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect-app": patch ---- - -Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs. - -Add tests covering: - -- `TaggedUnion` with `encodeKeys`-wrapped members -- `TaggedUnion` with `TaggedClass` members diff --git a/.changeset/calm-picks-refresh.md b/.changeset/calm-picks-refresh.md deleted file mode 100644 index b20592b13..000000000 --- a/.changeset/calm-picks-refresh.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Update Atom query caches in memory and accumulate stream invalidations so live events do not cause repeated RPC refetches. diff --git a/.changeset/chilly-colts-attack.md b/.changeset/chilly-colts-attack.md deleted file mode 100644 index 303d1065f..000000000 --- a/.changeset/chilly-colts-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -update dprint plugins diff --git a/.changeset/chilly-steaks-rule.md b/.changeset/chilly-steaks-rule.md deleted file mode 100644 index c7dd9521a..000000000 --- a/.changeset/chilly-steaks-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix input date diff --git a/.changeset/chubby-dolls-yell.md b/.changeset/chubby-dolls-yell.md deleted file mode 100644 index ed4649650..000000000 --- a/.changeset/chubby-dolls-yell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/infra": patch -"@effect-app/cli": patch ---- - -fix missing toCodecJson usages diff --git a/.changeset/class-facade-encode-keys.md b/.changeset/class-facade-encode-keys.md deleted file mode 100644 index 37d81fd7c..000000000 --- a/.changeset/class-facade-encode-keys.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/eslint-codegen-model": patch ---- - -Unify generated model facades on `OpaqueFacade` and support class facades through encoded-key schema transforms. diff --git a/.changeset/clean-atom-suspense.md b/.changeset/clean-atom-suspense.md deleted file mode 100644 index fac6a587e..000000000 --- a/.changeset/clean-atom-suspense.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix atom-query suspense cleanup so per-observer wrappers unsubscribe on unmount while cached query atoms keep their idle TTL. diff --git a/.changeset/clean-sql-data-column.md b/.changeset/clean-sql-data-column.md deleted file mode 100644 index 8d5798597..000000000 --- a/.changeset/clean-sql-data-column.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Strip `_etag` and `id` from the `data` JSON column in SQL store adapters (SQLite + PostgreSQL). These fields are already stored as dedicated columns and were redundantly duplicated inside the JSON blob. On read, `parseRow` now re-injects `id` from the row column. Backward compatible: existing rows with `_etag`/`id` in `data` continue to work as the column values take precedence. diff --git a/.changeset/clean-unused-dependencies.md b/.changeset/clean-unused-dependencies.md deleted file mode 100644 index 61fbe03d2..000000000 --- a/.changeset/clean-unused-dependencies.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Remove unused dependency declarations from package manifests. diff --git a/.changeset/cleanup-tsgolint-codegen.md b/.changeset/cleanup-tsgolint-codegen.md deleted file mode 100644 index 8a3965fd5..000000000 --- a/.changeset/cleanup-tsgolint-codegen.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Cleanup after tsgolint + oxlint-codegen-plugin migration: - -- Wire `@effect-app/eslint-codegen-model/oxlint` via `jsPlugins` object form (`{ name: "codegen", specifier: ... }`) so the `codegen/codegen` rule key resolves. -- Drop `eslint-plugin-codegen` dep, patch, and `augmentedConfig` helper — codegen now runs through oxlint. -- Break cyclic workspace dep between `eslint-codegen-model` and `eslint-shared-config`; remove dead `eslint.config.mjs` from `eslint-codegen-model`. -- Switch `@effect-app/vue` to oxlint-only (no `.vue` files in `src`); drop its ESLint config and `eslint-shared-config` devDep. -- Restore `@typescript-eslint` plugin and rules in shared `baseConfig` so inline `eslint-disable @typescript-eslint/...` directives resolve in `@effect-app/vue-components` (the only remaining ESLint consumer, for `.vue` files). -- Add `globals.browser` to `vueConfig` so browser globals (`window`, `console`, `URL`, etc.) resolve. diff --git a/.changeset/clear-cycles-enjoy.md b/.changeset/clear-cycles-enjoy.md deleted file mode 100644 index ff5f43093..000000000 --- a/.changeset/clear-cycles-enjoy.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update effect to latest beta diff --git a/.changeset/clever-breads-enter.md b/.changeset/clever-breads-enter.md deleted file mode 100644 index 41a139a79..000000000 --- a/.changeset/clever-breads-enter.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -fix request scope problems diff --git a/.changeset/clever-pets-follow.md b/.changeset/clever-pets-follow.md deleted file mode 100644 index 5e8281822..000000000 --- a/.changeset/clever-pets-follow.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -fix bs diff --git a/.changeset/cli-preserve-trailing-newline.md b/.changeset/cli-preserve-trailing-newline.md deleted file mode 100644 index 9ab097e6f..000000000 --- a/.changeset/cli-preserve-trailing-newline.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -preserve trailing newline when writing package.json files diff --git a/.changeset/cli-sync-no-tags.md b/.changeset/cli-sync-no-tags.md deleted file mode 100644 index 4ad0ab3d3..000000000 --- a/.changeset/cli-sync-no-tags.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Prevent subtree sync commands from importing upstream git tags. diff --git a/.changeset/cli-ue-semver-target.md b/.changeset/cli-ue-semver-target.md deleted file mode 100644 index 35a7c55e2..000000000 --- a/.changeset/cli-ue-semver-target.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Make `effa ue` update Effect and Effect App packages to the highest versions allowed by their existing semver ranges, including compatible prerelease updates. diff --git a/.changeset/client-fetch-property.md b/.changeset/client-fetch-property.md deleted file mode 100644 index d83dd4b70..000000000 --- a/.changeset/client-fetch-property.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Client entries are now plain objects; use `.fetch` to invoke the request. - -`client.Xxx` no longer is callable or an `Effect` itself. Call `client.Xxx.fetch(input)` (or `client.Xxx.fetch` for input-less requests) instead. `.mutate`, `.query`, `.suspense`, `.wrap`, and `.fn` are unchanged. diff --git a/.changeset/cluster-servicebus-notifications.md b/.changeset/cluster-servicebus-notifications.md deleted file mode 100644 index 2c0ba5b37..000000000 --- a/.changeset/cluster-servicebus-notifications.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add Azure Service Bus topic notifications for persisted cluster storage polling. diff --git a/.changeset/codegen-rule-options.md b/.changeset/codegen-rule-options.md deleted file mode 100644 index b47bbd83c..000000000 --- a/.changeset/codegen-rule-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Allow oxlint codegen rule options so shared preset defaults can be configured. diff --git a/.changeset/cold-brooms-ski.md b/.changeset/cold-brooms-ski.md deleted file mode 100644 index e8ee195ab..000000000 --- a/.changeset/cold-brooms-ski.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Improve `deepToRaw` to support any root input type, correctly unwrap refs/computed values, and preserve collection/date shapes (`Array`, `Map`, `Set`, `Date`) while deeply removing Vue reactivity wrappers. diff --git a/.changeset/cold-rabbits-pay.md b/.changeset/cold-rabbits-pay.md deleted file mode 100644 index 29c55138e..000000000 --- a/.changeset/cold-rabbits-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -use uninterruptiple diff --git a/.changeset/command-button-optional-input.md b/.changeset/command-button-optional-input.md deleted file mode 100644 index 64c68eb33..000000000 --- a/.changeset/command-button-optional-input.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": minor ---- - -`CommandButton`: add `optionalInput` prop accepting `Option`. `Some` enables button and fires click with value; `None` disables. Use when input is gated by a `computed` rather than a `v-if` on the button. Also bind `:disabled` on the `` (previously only `aria-disabled`). diff --git a/.changeset/commander-state-toast-types.md b/.changeset/commander-state-toast-types.md deleted file mode 100644 index 264417ccb..000000000 --- a/.changeset/commander-state-toast-types.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix Commander combinator type inference for void Arg and withDefaultToast callbacks - -- Use `ArgForCombinator` helper to properly resolve `void` args to `undefined` in combinator positions, enabling correct type inference for `withDefaultToast` and other curried combinators -- Use explicit positional params in `withDefaultToast` options callbacks instead of rest spread, allowing users to omit trailing parameters diff --git a/.changeset/commander-stream-fn.md b/.changeset/commander-stream-fn.md deleted file mode 100644 index c67a54558..000000000 --- a/.changeset/commander-stream-fn.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add `Command.streamFn` — a stream-backed variant of `Command.fn`. - -The body generator (or plain function) returns a `Stream` instead of an `Effect`. The command's `waiting` state stays `true` while the stream is running and updates the reactive `result` ref for every emitted value. - -Three handler shapes are accepted: - -1. **Generator returning a Stream** (primary): - ```ts - Command.streamFn("exportData")( - function*(arg, ctx) { - const token = yield* getAuthToken - return Stream.fromEffect(startExport(token, arg.id)).pipe( - Stream.flatMap((job) => pollProgress(job.id)) - ) - } - ) - ``` -2. Function returning a `Stream` directly. -3. Function returning `Effect` (unwrapped automatically). diff --git a/.changeset/common-schools-stop.md b/.changeset/common-schools-stop.md deleted file mode 100644 index ccf5fa437..000000000 --- a/.changeset/common-schools-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix more branded types diff --git a/.changeset/computed-projections-extensions.md b/.changeset/computed-projections-extensions.md deleted file mode 100644 index fdb45a612..000000000 --- a/.changeset/computed-projections-extensions.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Extend computed projections with additional relation operators: - -- `relation(path).every(op)` — boolean, all elements match the filter (compiled as `NOT EXISTS(... WHERE NOT (filter))`). -- `relation(path).distinctCount(field, op?)` — distinct count of values at `field` within the relation. -- `relation(path).sum(field, op?)` — numeric sum over a relation field. -- `relation(path).collect(field, op?)` / `collectDistinct(field, op?)` — collect values into an array (with optional dedup). - -All operators compile to native subqueries on SQL (sqlite/pg) and Cosmos, and to in-memory equivalents on the Memory store. diff --git a/.changeset/computed-projections-infra.md b/.changeset/computed-projections-infra.md deleted file mode 100644 index 903dbd32a..000000000 --- a/.changeset/computed-projections-infra.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add computed projections for repository queries with adapter-level compilation for memory, SQL, and Cosmos stores. diff --git a/.changeset/concurrent-decode-helpers.md b/.changeset/concurrent-decode-helpers.md deleted file mode 100644 index 752b2d5f6..000000000 --- a/.changeset/concurrent-decode-helpers.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch -"@effect-app/cli": patch ---- - -Add concurrent decode helper APIs and migrate decode callsites to use them. - -- Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. -- Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. -- Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. -- Keep schema constructors free of hardcoded parse concurrency overrides. diff --git a/.changeset/config-nested-native.md b/.changeset/config-nested-native.md deleted file mode 100644 index 7d9cea505..000000000 --- a/.changeset/config-nested-native.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Remove the local `Config.nested` / `ConfigProvider.nested` overrides. They worked around an effect v3 bug where the nested namespace segment bypassed the provider's `mapInput`/`constantCase` transform. effect `4.0.0-beta.84` fixes this upstream (its transformation-compose refactor threads the nested prefix through the provider's lookup transform), and the old overrides relied on internals (`Config.make`, `provider.get`/`mapInput`/`prefix`) that no longer exist. Both modules now re-export effect directly; behaviour for consumers (e.g. `Config.nested("cups")` against a `constantCase` env provider) is unchanged and correct. diff --git a/.changeset/config.json b/.changeset/config.json index f246cc8e0..de158b048 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,17 +1,55 @@ { - "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json", - "changelog": "@changesets/cli/changelog", + "$schema": "https://unpkg.com/@changesets/config@4.0.0-next.8/schema.json", + "changelog": ["@changesets/changelog-github", { "repo": "Effect-TS/effect" }], "commit": false, + "format": false, + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [ + "scratchpad", + "scripts" + ], + "privatePackages": false, "fixed": [ [ - "effect-app", - "@effect-app/infra", - "@effect-app/vue", - "@effect-app/vue-components" + "effect", + "@effect/ai-anthropic", + "@effect/ai-cloudflare", + "@effect/ai-openai", + "@effect/ai-typesafe", + "@effect/ai-openai-compat", + "@effect/ai-openrouter", + "@effect/atom-react", + "@effect/atom-solid", + "@effect/atom-vue", + "@effect/docgen", + "@effect/doctest", + "@effect/openapi-generator", + "@effect/opentelemetry", + "@effect/platform-browser", + "@effect/platform-bun", + "@effect/platform-deno", + "@effect/platform-node", + "@effect/platform-node-shared", + "@effect/sql-clickhouse", + "@effect/sql-d1", + "@effect/sql-libsql", + "@effect/sql-mssql", + "@effect/sql-mysql2", + "@effect/sql-pg", + "@effect/sql-pglite", + "@effect/sql-sqlite-bun", + "@effect/sql-sqlite-do", + "@effect/sql-sqlite-node", + "@effect/sql-sqlite-react-native", + "@effect/sql-sqlite-wasm", + "@effect/vitest" ] ], - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "patch" + "snapshot": { + "useCalculatedVersion": false, + "prereleaseTemplate": "{tag}-{commit}" + } } diff --git a/.changeset/configurable-barrel-import-extension.md b/.changeset/configurable-barrel-import-extension.md deleted file mode 100644 index 68fe4c1da..000000000 --- a/.changeset/configurable-barrel-import-extension.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": minor ---- - -Add `importExtension` option to the `barrel` preset to control the file extension emitted on generated imports/exports (defaults to `.js`). Configurable per block, globally via the oxlint rule option `["error", { barrel: { importExtension: ".ts" } }]`, or via `codegen.config.json` (CLI also accepts `--config `). Resolution order: preset default → global defaults → per-block options. diff --git a/.changeset/configurable-mutation-invalidation.md b/.changeset/configurable-mutation-invalidation.md deleted file mode 100644 index 50e94fa19..000000000 --- a/.changeset/configurable-mutation-invalidation.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Make the default mutation invalidation heuristic configurable. - -The built-in default is unchanged (collapse one namespace level: `Foo/Bar.x` invalidates `["$Foo"]`, `Foo/Bar/Baz.x` invalidates `["$Foo","$Bar"]`). Client projects can now override it globally: - -```ts -import { makeQueryKey, setDefaultGetQueryKey } from "@effect-app/vue" - -// invalidate the full namespace of the action (no parent collapse) -setDefaultGetQueryKey((h) => { - const key = makeQueryKey(h) - const ns = key.filter((_) => _.startsWith("$")) - if (!ns.length) throw new Error("empty query key for: " + h.id) - return ns -}) -``` - -Call at app bootstrap. Pass `undefined` to restore the built-in default. Per-mutation overrides via the existing `queryInvalidation` option still take precedence. diff --git a/.changeset/context-map-resolver-cache.md b/.changeset/context-map-resolver-cache.md deleted file mode 100644 index cb932fa44..000000000 --- a/.changeset/context-map-resolver-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add `withRequestResolverCache` to scope RequestResolver cache to the ContextMap (request-scoped). Add `getOrCreateStore` to ContextMap for generic scoped storage. diff --git a/.changeset/cool-needles-wash.md b/.changeset/cool-needles-wash.md deleted file mode 100644 index 79bc5b986..000000000 --- a/.changeset/cool-needles-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": minor ---- - -chore: update @tanstack/vue-form to version 1.32.0 and refactor OmegaTaggedUnion component to use 'tag' instead of 'state' diff --git a/.changeset/core-boundary-split.md b/.changeset/core-boundary-split.md deleted file mode 100644 index 34386436a..000000000 --- a/.changeset/core-boundary-split.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": major -"@effect-app/vue": minor ---- - -Move core service contracts and runtime-agnostic modules into `effect-app`, keep `infra` and `vue` focused on adapters, and drop the temporary `infra` compatibility re-export paths in favor of the new canonical imports. - -`@effect-app/infra` no longer re-exports moved core modules such as `./Model`, `./Emailer/service`, `./QueueMaker/service`, `./Store/service`, `./adapters/*`, or `./api/*` entrypoints. diff --git a/.changeset/cosmos-autoscale-containers.md b/.changeset/cosmos-autoscale-containers.md deleted file mode 100644 index 88c7d900a..000000000 --- a/.changeset/cosmos-autoscale-containers.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -Add opt-in `autoscaleMaxThroughput` for Cosmos containers created on demand (`StorageConfig`, `ClusterCosmosConfig`, `WorkflowEngineCosmosConfig`). When set, a missing container is created with autoscale at that max RU/s, unless its database has shared throughput (then it keeps sharing the database pool). Existing containers are untouched; unset keeps the Cosmos default of manual 400 RU/s per container. diff --git a/.changeset/cosmos-cluster-occ-tests.md b/.changeset/cosmos-cluster-occ-tests.md deleted file mode 100644 index 700a8e83e..000000000 --- a/.changeset/cosmos-cluster-occ-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Tighten Cosmos cluster OCC handling for concurrent node polling. diff --git a/.changeset/cosmos-every-param-shift.md b/.changeset/cosmos-every-param-shift.md deleted file mode 100644 index 9d117caf5..000000000 --- a/.changeset/cosmos-every-param-shift.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix Cosmos `projectComputed` parameter index shift when `relation(...).every(...)` is present. The shared filter `print` was invoked twice per `relation-every` (once eagerly for the `where` variable, again inside the `NOT EXISTS(... WHERE NOT (...))` branch), bumping the outer `@v` counter beyond the bound parameter array and producing SQL that referenced unbound placeholders — queries returned 0 rows on Cosmos while SQLite/Memory adapters were unaffected. diff --git a/.changeset/cosmos-filter-seed.md b/.changeset/cosmos-filter-seed.md deleted file mode 100644 index 989ba7388..000000000 --- a/.changeset/cosmos-filter-seed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix CosmosDB store `filter` to trigger namespace seeding on first access. Previously, if `filter` was the first operation called on a namespace, seed data was never created. diff --git a/.changeset/cosmos-namespace-spans.md b/.changeset/cosmos-namespace-spans.md deleted file mode 100644 index f8da1f307..000000000 --- a/.changeset/cosmos-namespace-spans.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add span to Cosmos `batchRemove` and include `namespace` attribute on all Cosmos store spans. diff --git a/.changeset/cosmos-namespace-support.md b/.changeset/cosmos-namespace-support.md deleted file mode 100644 index 296f62a4a..000000000 --- a/.changeset/cosmos-namespace-support.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add storage namespace support to CosmosDB adapter via partition key prefixing. When `allowNamespace` is configured, the namespace from `storeId` is prepended to partition key values (e.g., `test-ns::primary`), isolating data per namespace within the same container. diff --git a/.changeset/cosmos-span-response-size.md b/.changeset/cosmos-span-response-size.md deleted file mode 100644 index 3787b60ff..000000000 --- a/.changeset/cosmos-span-response-size.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Annotate Cosmos read spans with response size, resource count, and request charge. - -`Cosmos.queryRaw`, `Cosmos.all`, `Cosmos.filter`, and `Cosmos.find` now set `db.cosmos.request_charge`, `db.cosmos.response_bytes`, and (where applicable) `db.cosmos.resource_count` on the active span. Bytes are sourced from `diagnostics.clientSideRequestStatistics.totalResponsePayloadLengthInBytes` — no payload stringification. diff --git a/.changeset/cosmos-workflow-engine.md b/.changeset/cosmos-workflow-engine.md deleted file mode 100644 index 07d029166..000000000 --- a/.changeset/cosmos-workflow-engine.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add Cosmos DB backed `WorkflowEngine` adapter (`layerCosmos` in `WorkflowEngineCosmos.ts`). Persists workflow state in a single container partitioned by `executionId` so per-execution writes share a partition key (TransactionalBatch-eligible). Optimistic concurrency via `_etag` + `IfMatch` on Replace; first-writer-wins via create-only batch ops for activity results and durable-deferred completions; a persisted *suspended* activity is overwritten via upsert on resume. Values crossing the storage boundary round-trip through `Schema` codecs (`S.fromJsonString(S.toCodecJson(...))`) using the workflow's own `payloadSchema` / `successSchema` / `errorSchema` for typed values, and the cluster engine's opaque `AnyOrVoid` codec for activity / deferred payloads. Includes time-bound lease + heartbeat fiber, scope-bound recovery poller for crashed-driver takeover, and cross-partition clock poller for restart-survivable durable timers. diff --git a/.changeset/cozy-cats-lie.md b/.changeset/cozy-cats-lie.md deleted file mode 100644 index 0b55ecb06..000000000 --- a/.changeset/cozy-cats-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -doc span diff --git a/.changeset/crisp-seals-care.md b/.changeset/crisp-seals-care.md deleted file mode 100644 index 8ecdea9fd..000000000 --- a/.changeset/crisp-seals-care.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Beta25 diff --git a/.changeset/crisp-trees-smash.md b/.changeset/crisp-trees-smash.md deleted file mode 100644 index 43f67359d..000000000 --- a/.changeset/crisp-trees-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -cleanup diff --git a/.changeset/ctxmap-per-request-memomap.md b/.changeset/ctxmap-per-request-memomap.md deleted file mode 100644 index 6cf5beab8..000000000 --- a/.changeset/ctxmap-per-request-memomap.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `provideOnRequestScope` leaking a single `ContextMap` across concurrent requests. - -`Layer.buildWithScope(layer, requestScope)` resolves its `MemoMap` from the -ambient fiber context, which lives on the HTTP server fiber and is therefore -shared by every request that server handles. With the resulting memoization, -the first request to land on a freshly-started server built -`ContextMapContainer.layer` once; every subsequent overlapping request received -the same `ContextMap` instance — etags written by one request were observed -(or overwritten) by another, and the finalizer was anchored to the first -request's scope. - -`provideOnRequestScope` now allocates a fresh `MemoMap` per call via -`Layer.makeMemoMap` and builds with `Layer.buildWithMemoMap(layer, memoMap, -requestScope)`. Each request gets its own `ContextMap`, the request-scope -binding from the earlier SSE fix is preserved, and the finalizer still only -fires once the response body has fully drained. - -Adds regression coverage in `rpc-context-map-streaming.test.ts` for three -properties: mid-stream survival of ContextMap state, a fresh map on each -succeeding request, and isolation between overlapping concurrent requests. diff --git a/.changeset/cuddly-laws-smell.md b/.changeset/cuddly-laws-smell.md deleted file mode 100644 index aeef529ec..000000000 --- a/.changeset/cuddly-laws-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Use warning toasts for expected commander failures, while keeping unexpected errors as error toasts. diff --git a/.changeset/cups-error-tagged.md b/.changeset/cups-error-tagged.md deleted file mode 100644 index b84f4058e..000000000 --- a/.changeset/cups-error-tagged.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Wrap `CUPS` exec failures in tagged `CUPSError` instead of `UnknownException`. - -Retains `command`, `message`, exit `code`, `signal`, `killed`, `stdout`, -`stderr`, and original `cause` from the underlying `child_process.exec` -rejection so callers can branch on real failure detail. diff --git a/.changeset/curly-beds-sing.md b/.changeset/curly-beds-sing.md deleted file mode 100644 index bf39e86ea..000000000 --- a/.changeset/curly-beds-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Use request-type RPC annotations to drive interruptibility and retry behavior, apply command uninterruptibility through the router wrapper path, and add an in-memory E2E test covering command vs query interruption behavior. diff --git a/.changeset/curly-dolls-double.md b/.changeset/curly-dolls-double.md deleted file mode 100644 index 68be424ab..000000000 --- a/.changeset/curly-dolls-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm now runs field validation in ordered composition: OmegaForm-generated field rules first, then original schema checks. This makes custom schema filters (for example `S.makeFilter` checks) show during `onChange`/`onBlur` while preserving existing OmegaForm validation behavior and messages. diff --git a/.changeset/curvy-files-share.md b/.changeset/curvy-files-share.md deleted file mode 100644 index 953189ac0..000000000 --- a/.changeset/curvy-files-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -revert bs diff --git a/.changeset/curvy-pugs-rush.md b/.changeset/curvy-pugs-rush.md deleted file mode 100644 index 5bc6684cc..000000000 --- a/.changeset/curvy-pugs-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `Repository.query` environment typing to exclude schema context provided through `makeRepo` options. diff --git a/.changeset/cyan-experts-smoke.md b/.changeset/cyan-experts-smoke.md deleted file mode 100644 index c1646a383..000000000 --- a/.changeset/cyan-experts-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -render diff --git a/.changeset/cyan-pianos-peel.md b/.changeset/cyan-pianos-peel.md deleted file mode 100644 index 97bbec1ce..000000000 --- a/.changeset/cyan-pianos-peel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -fix stream type when no success specified diff --git a/.changeset/cyan-things-train.md b/.changeset/cyan-things-train.md deleted file mode 100644 index 27ca86655..000000000 --- a/.changeset/cyan-things-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix missing overloads diff --git a/.changeset/date-defaults-pipe-self.md b/.changeset/date-defaults-pipe-self.md deleted file mode 100644 index f66426fbc..000000000 --- a/.changeset/date-defaults-pipe-self.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch ---- - -Fix `Date` / `DateValid` default helpers to pipe from their own schema. - -`withConstructorDefault` and `withDecodingDefaultType` on `DateValid` previously piped from `DateFromString`, dropping the `isDateValid()` check (and, after the recent identifier split, the `DateValid` annotations) from the resulting defaulted schema. Both `Date` and `DateValid` now use `extendM` so the helpers attach to the underlying schema (`DateFromString` / `DateValidFromString`). diff --git a/.changeset/date-identifier-split.md b/.changeset/date-identifier-split.md deleted file mode 100644 index fc385516d..000000000 --- a/.changeset/date-identifier-split.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch ---- - -Distinguish `Date` and `DateValid` in JSON Schema output. - -- `Date` now emits identifier `DateOrInvalid` with description noting the value may be invalid. -- `DateValid` now emits its own annotated string (identifier `Date`) with description stating a valid ISO 8601 date is required. diff --git a/.changeset/delay-waiting-toast.md b/.changeset/delay-waiting-toast.md deleted file mode 100644 index 64ab53079..000000000 --- a/.changeset/delay-waiting-toast.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Delay the in-progress (waiting) toast by 1 second in `withToast` and `withDefaultToastStream`. Fast operations that produce a success/failure (or, for streams, a progress event or terminal state) within the delay window never show a waiting toast at all. Any subsequent waiting/progress/success/failure toast aborts the pending delayed toast so it never flashes after the terminal state. diff --git a/.changeset/disable-query-invalidation.md b/.changeset/disable-query-invalidation.md deleted file mode 100644 index 812ede95f..000000000 --- a/.changeset/disable-query-invalidation.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"effect-app": minor -"@effect-app/vue": minor ---- - -Add `disableQueryInvalidation` flag to Command config for background saves. - -Set `disableQueryInvalidation: true` in a `Req.Command` config (3rd argument) -to suppress all client-side query invalidation for that command — client -`invalidatesQueries` callbacks, server-returned `metadata.invalidateQueries`, -and repository-derived write-dependency matching are all skipped. Use for -background saves (e.g. debounced auto-save) whose writes should not trigger -query refetches. - -- `InvalidationConfig` gains `disableQueryInvalidation?: boolean`. -- `RequestHandlerWithInput` gains `disableQueryInvalidation?: boolean`, - propagated from `Request.config` by `ApiClientFactory.makeFor`. -- `invalidateCache` early-returns `Effect.void` when the flag is set, - applying to both regular and stream mutations. diff --git a/.changeset/document-query-project-modes.md b/.changeset/document-query-project-modes.md deleted file mode 100644 index 176eb516e..000000000 --- a/.changeset/document-query-project-modes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Document `Q.project` modes (`transform` / `project` / `collect`): how `select` is derived from the schema AST, the PM reverse-mapping behaviour per mode, and which decode errors surface vs `orDie`. diff --git a/.changeset/drop-constructor-default.md b/.changeset/drop-constructor-default.md deleted file mode 100644 index 41cdd1ccb..000000000 --- a/.changeset/drop-constructor-default.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch ---- - -Add `Schema.dropConstructorDefault`, a `Struct.Lambda` that clears a field's `withConstructorDefault`. Compose it with `Struct.omit`/`Struct.map` when deriving a partial-update schema from a "create" schema, so omitted fields' constructor defaults don't resurrect themselves in `.make(...)` output. - -Also, `makeExactOptional` now drops constructor defaults automatically, matching this behavior. diff --git a/.changeset/easy-aliens-peel.md b/.changeset/easy-aliens-peel.md deleted file mode 100644 index 8db9c8e07..000000000 --- a/.changeset/easy-aliens-peel.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update packages diff --git a/.changeset/easy-spiders-study.md b/.changeset/easy-spiders-study.md deleted file mode 100644 index 1addf28df..000000000 --- a/.changeset/easy-spiders-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix: clientFor diff --git a/.changeset/effa-sync-shared.md b/.changeset/effa-sync-shared.md deleted file mode 100644 index 8a0f1b666..000000000 --- a/.changeset/effa-sync-shared.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"@effect-app/cli": minor ---- - -Add `effa sync` / `effa sync-diff` / `effa sync-push` subcommands for syncing content (architecture docs, e2e helpers, ts-plugins) from `effect-app/shared` into consuming projects per a project-side `.shared.json` lockfile. - -- `effa sync` — clone/checkout the shared repo at the pinned ref into `~/.cache/effa/shared/`, then copy the artifact map's files into the project (honoring `exclude`). -- `effa sync-diff` — sha256 compare each tracked file against the cache copy; reports `M` (modified locally), `D` (missing from project), `E` (excluded). -- `effa sync-push [--pr] [-m msg] [--branch name]` — branch in the cache off the pinned ref, copy modified project files in, commit, push. Optional `--pr` opens a PR via `gh pr create`. - -Lockfile shape: - -```json -{ - "repo": "github.com/effect-app/shared", - "ref": "", - "artifacts": { "": "" }, - "exclude": [""] -} -``` diff --git a/.changeset/effa-sync-update-flag.md b/.changeset/effa-sync-update-flag.md deleted file mode 100644 index dd4e975e9..000000000 --- a/.changeset/effa-sync-update-flag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Add `effa sync --update [--ref ]` to bump the pinned `ref` in `.shared.json` to the latest sha before syncing. Without `--ref` it resolves the shared repo's default branch HEAD; with `--ref` it resolves that ref (branches resolved against `origin/` so you get the latest remote commit). The resolved sha is written back to the lockfile. Omitting `--update` keeps the existing pin-and-sync behaviour. diff --git a/.changeset/effect-app-arbitrary-email-phone.md b/.changeset/effect-app-arbitrary-email-phone.md deleted file mode 100644 index 5dca48af1..000000000 --- a/.changeset/effect-app-arbitrary-email-phone.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -`Email` and `PhoneNumber` carry native `arbitraryConstraint` patterns, so `effect/unstable/arbitrary` (and `generateFromSchema`) can sample them instead of exhausting rejection sampling on their `refine` guards. diff --git a/.changeset/effect-app-prefer-fn-untraced.md b/.changeset/effect-app-prefer-fn-untraced.md deleted file mode 100644 index ef141255c..000000000 --- a/.changeset/effect-app-prefer-fn-untraced.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Replace `(...) => Effect.gen` with `Effect.fnUntraced` in `apiClientFactory`. diff --git a/.changeset/effect-app-realistic-number-samples.md b/.changeset/effect-app-realistic-number-samples.md deleted file mode 100644 index 33b9fcded..000000000 --- a/.changeset/effect-app-realistic-number-samples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -`PositiveNumber` and `NonNegativeNumber` bound native Arbitrary generation to at most 1,000,000 (`arbitraryConstraint` on their existing checks), so generated fixtures no longer overflow derived totals to `Infinity`. Validation and JSON Schema output are unchanged. diff --git a/.changeset/effect-main-rc-apis.md b/.changeset/effect-main-rc-apis.md deleted file mode 100644 index fee3fcdd4..000000000 --- a/.changeset/effect-main-rc-apis.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect-app": patch -"@effect-app/cli": patch ---- - -Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - -Breaking API updates from this RC: - -- PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) -- `Config.Record` now returns a Config -- Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` -- `SchemaTransformation.transformOrFail` renamed to `transformEffect` -- `Fiber.currentSpan` moved to `fiber.cache.span` -- HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` -- Object JSON Schema now emits `additionalProperties: true` -- Schema `Union` AST stores matching as `options.mode` instead of `mode` diff --git a/.changeset/eighty-aliens-hide.md b/.changeset/eighty-aliens-hide.md deleted file mode 100644 index 36e346779..000000000 --- a/.changeset/eighty-aliens-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -fix conversion diff --git a/.changeset/eighty-signs-smile.md b/.changeset/eighty-signs-smile.md deleted file mode 100644 index 02133b538..000000000 --- a/.changeset/eighty-signs-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix type make interface diff --git a/.changeset/eleven-eyes-lie.md b/.changeset/eleven-eyes-lie.md deleted file mode 100644 index 4b0d8cac3..000000000 --- a/.changeset/eleven-eyes-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -update diff --git a/.changeset/empty-queens-begin.md b/.changeset/empty-queens-begin.md deleted file mode 100644 index 64f54460e..000000000 --- a/.changeset/empty-queens-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix: prevent expansion of nominal brands diff --git a/.changeset/empty-rules-check.md b/.changeset/empty-rules-check.md deleted file mode 100644 index 57d63a3f9..000000000 --- a/.changeset/empty-rules-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -remove unnecessary span? diff --git a/.changeset/eslint-codegen-glob-11.md b/.changeset/eslint-codegen-glob-11.md deleted file mode 100644 index f2e7e8ab9..000000000 --- a/.changeset/eslint-codegen-glob-11.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Migrate `glob` 8 → 11. Use the named `globSync` export (glob 11 dropped the default export, which broke loading the package as an ESM oxlint plugin), and drop the stale `@types/glob` (glob 11 ships its own types). Also clears the package's pre-existing implicit-`any` typecheck errors. diff --git a/.changeset/expose-make-on-query-command.md b/.changeset/expose-make-on-query-command.md deleted file mode 100644 index 048fdd196..000000000 --- a/.changeset/expose-make-on-query-command.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Expose `make`, `makeOption`, and `makeEffect` static helpers on request classes created via `Query`/`Command`. diff --git a/.changeset/extract-fixed-nuxt-error-boundary.md b/.changeset/extract-fixed-nuxt-error-boundary.md deleted file mode 100644 index 8e4955146..000000000 --- a/.changeset/extract-fixed-nuxt-error-boundary.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Add `FixedNuxtErrorBoundary`, extracted from duplicated per-project copies. Wraps Nuxt's error boundary with injectable `captureException`/`toastError`/`debug` props, distinguishes supported errors (setup/template) from unsupported ones (native event handlers, reported but not rendered), ignores interrupts-only Effect `CauseException` failures, and clears itself on route change. diff --git a/.changeset/facade-hardening-error-facade.md b/.changeset/facade-hardening-error-facade.md deleted file mode 100644 index 8d051177d..000000000 --- a/.changeset/facade-hardening-error-facade.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"effect-app": patch -"@effect-app/eslint-codegen-model": patch ---- - -Harden model facades and add `OpaqueErrorFacadeClass`. - -- `OpaqueErrorFacadeClass`: facade `TaggedErrorClass`/`ErrorClass` while keeping - `Cause.YieldableError` on the constructed instance (so `yield* new Err()`, - `Effect.fail`, and `instanceof` keep working through the facade). -- `OpaqueFacadeInput` relaxed to require only the codec service channels, so - transformed schemas (`.pipe(encodeKeys/annotate/filter/...)`) can be facaded; - `fields`/`copy`/`mapFields` flow through `OpaqueFacadeStatics` when present, and - `to` is carried so models that compose via `X.to.fields` keep working. -- Dropped the wide `fields`/`mapFields` overrides on the facade interfaces so the - precise statics win (keeps `Q.project(X.mapFields(...))` typed). -- Codegen (`eslint-codegen-model`): resolver prefers the private `_X` over the - self-referential facade; converges static service types in one run; per-model - classification (facade Opaque models, leave `Class` standard in mixed files); - `Make` emitted as `type X = {...} | void` when the make-input is voidable; - `readonly`-prefixed array/tuple elements parenthesized; value self-references in - the moved `_X` body rewritten to `_X`; instance getters surface on `Self`. diff --git a/.changeset/fair-baboons-marry.md b/.changeset/fair-baboons-marry.md deleted file mode 100644 index ffc987772..000000000 --- a/.changeset/fair-baboons-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Migrate schema arbitrary annotations to Effect v4 `toArbitrary` and cover `StringId` generation with a fast-check test. diff --git a/.changeset/fair-oranges-wave.md b/.changeset/fair-oranges-wave.md deleted file mode 100644 index 6ddb19327..000000000 --- a/.changeset/fair-oranges-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Preserve Struct.copy through `annotate`, `annotateKey`, and `mapFields` chains, and add tests covering chained copy behavior on Struct schemas. diff --git a/.changeset/fair-tables-march.md b/.changeset/fair-tables-march.md deleted file mode 100644 index 6b2447654..000000000 --- a/.changeset/fair-tables-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix SSE event stream merge in pipe usage diff --git a/.changeset/famous-plums-watch.md b/.changeset/famous-plums-watch.md deleted file mode 100644 index 33f059b07..000000000 --- a/.changeset/famous-plums-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fixes Redacted casting diff --git a/.changeset/fancy-ducks-run.md b/.changeset/fancy-ducks-run.md deleted file mode 100644 index fc4ccf31a..000000000 --- a/.changeset/fancy-ducks-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -fix queryresources diff --git a/.changeset/fast-loops-scream.md b/.changeset/fast-loops-scream.md deleted file mode 100644 index 0b165758e..000000000 --- a/.changeset/fast-loops-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fixes error handling diff --git a/.changeset/fiery-feet-sleep.md b/.changeset/fiery-feet-sleep.md deleted file mode 100644 index e8da517dd..000000000 --- a/.changeset/fiery-feet-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix type diff --git a/.changeset/fiery-suits-kneel.md b/.changeset/fiery-suits-kneel.md deleted file mode 100644 index 302af0508..000000000 --- a/.changeset/fiery-suits-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -workaround effect error messages diff --git a/.changeset/five-trams-repeat.md b/.changeset/five-trams-repeat.md deleted file mode 100644 index 0e00fe7c9..000000000 --- a/.changeset/five-trams-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix bs diff --git a/.changeset/fix-class-type-export.md b/.changeset/fix-class-type-export.md deleted file mode 100644 index 2f2b0f7ad..000000000 --- a/.changeset/fix-class-type-export.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": minor ---- - -Add `copy` property to `Class` and `TaggedClass` for creating modified instances with updated fields. The `copy` method is cached per-class and supports both object and function-based updates, with pipeline support. diff --git a/.changeset/fix-cli-gist-cache-id.md b/.changeset/fix-cli-gist-cache-id.md deleted file mode 100644 index 7ff26aabf..000000000 --- a/.changeset/fix-cli-gist-cache-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Require an explicit `EFFA_GIST_CACHE_ID`, remove cache Gist discovery and automatic creation, and initialize missing company pointer files once. diff --git a/.changeset/fix-client-input-stream.md b/.changeset/fix-client-input-stream.md deleted file mode 100644 index a42f7e072..000000000 --- a/.changeset/fix-client-input-stream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix `client[Key].Input` resolving to `never` for stream request handlers. The extractor only matched `RequestHandlerWithInput`, so any `RequestStreamHandlerWithInput` entry fell through to the `never` branch. Added a parallel `RequestStreamHandlerWithInput` extract so stream Inputs surface the handler's input type. diff --git a/.changeset/fix-contextmap-leak.md b/.changeset/fix-contextmap-leak.md deleted file mode 100644 index 5a57edfe5..000000000 --- a/.changeset/fix-contextmap-leak.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `ContextMap` memory leak: tie its lifecycle to the request scope. - -`ContextMapContainer.layer` provides a fresh `ContextMap` per request via `Layer.effect`, but the inner `etags` and `store` maps were never released. Cached `RequestResolver` entries (and anything they closed over) stayed reachable as long as any fiber held a reference to the map, even after the request scope closed. - -`ContextMap` is now built with `Effect.acquireRelease`, and `makeContextMap` exposes a `clear()` finalizer that empties both maps when the request scope closes. `Layer.effect` strips the `Scope` requirement automatically in Effect v4. diff --git a/.changeset/fix-cosmos-cluster-deferred.md b/.changeset/fix-cosmos-cluster-deferred.md deleted file mode 100644 index 3cc53e00f..000000000 --- a/.changeset/fix-cosmos-cluster-deferred.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix Cosmos cluster workflow resume after durable deferred completion. diff --git a/.changeset/fix-cosmos-cluster-machineid.md b/.changeset/fix-cosmos-cluster-machineid.md deleted file mode 100644 index 74fafadd6..000000000 --- a/.changeset/fix-cosmos-cluster-machineid.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix machine-id allocation in the Cosmos cluster `RunnerStorage`. It derived the machine id from a 31-multiplier string hash of the runner address, so two distinct runners could hash to the same value mod 1024 — and since the machine id feeds the Snowflake generator mod 1024, those runners would emit colliding Snowflake ids (corrupting request/reply identity across the cluster). - -Machine ids are now allocated from a single counter document via an atomic server-side `incr`, mirroring SQL's auto-increment `machine_id` primary key: unique across distinct runners and stable per address (a re-registering runner reuses its persisted id). Verified against a live Cosmos account. diff --git a/.changeset/fix-cosmos-serverless-create.md b/.changeset/fix-cosmos-serverless-create.md deleted file mode 100644 index 074773f62..000000000 --- a/.changeset/fix-cosmos-serverless-create.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Create missing Cosmos containers without throughput when the account is serverless (offer read/replace returns 400). Provisioned databases still get autoscale when configured. diff --git a/.changeset/fix-cosmos-workflow-engine.md b/.changeset/fix-cosmos-workflow-engine.md deleted file mode 100644 index 1fe5da8d7..000000000 --- a/.changeset/fix-cosmos-workflow-engine.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix correctness bugs in the Cosmos `WorkflowEngine`, found by running the adapter against a real Cosmos account. The Cosmos workflow engine now passes the same conformance suite as the in-memory and SQLite engines. - -- **OCC conflicts were fatal.** `replaceExec` used `Effect.promise` + a `statusCode` check, but single-item Cosmos `replace` *throws* on 409/412/404 (only `read` and batch ops surface the code), so every conflict became an unrecoverable defect and the `OptimisticConcurrencyException` catch was dead code. It now uses `Effect.tryPromise` and matches the thrown error (mirrors `ClusterCosmos`), so lease claims, completions, and interrupts lose gracefully under contention. -- **Illegal resource ids.** Activity/deferred/clock doc ids embedded workflow/deferred names containing `/`, which Cosmos rejects. Ids are now URI-encoded via `cosmosId` (mirrors `ClusterCosmos`). -- **Interrupt could be lost under Cosmos latency.** A concurrent `interrupt` racing a suspending driver's `onComplete` could have its `interrupted` flag swallowed (OCC) or downgraded, leaving the re-drive unable to collapse the suspension. `interrupt`/`interruptUnsafe` now persist the flag with OCC retry (`markInterrupted`), and `onComplete` never downgrades a persisted `interrupted: true`. -- **`execute` now drives unconditionally** (matching SQLite), letting `drive`'s own guard short-circuit a running/completed fiber and re-drive a suspended one, instead of skipping re-drive when a stale local entry existed. diff --git a/.changeset/fix-is-generator-function.md b/.changeset/fix-is-generator-function.md deleted file mode 100644 index 67db4cd7f..000000000 --- a/.changeset/fix-is-generator-function.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Fix `isGeneratorFunction` using `isObject` instead of `isFunction`: generator functions have `typeof === "function"`, not `"object"`, so the check always returned `false`. This caused `Command.streamFn` generator-form handlers to silently pass a raw `Generator` object rather than an `Effect`, meaning the mutation was never executed. diff --git a/.changeset/fix-json-schema-annotations.md b/.changeset/fix-json-schema-annotations.md deleted file mode 100644 index 65946a1e3..000000000 --- a/.changeset/fix-json-schema-annotations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Fix JSON schema output for Email, Date, PhoneNumber, and Url schemas. The `jsonSchema` annotation key is not recognized by Effect v4's JSON schema generator — use proper v4 annotations (`format`, `description`) and built-in checks (`isMinLength`, `isMaxLength`) instead. diff --git a/.changeset/fix-jsonschema-postprocess.md b/.changeset/fix-jsonschema-postprocess.md deleted file mode 100644 index 025ef165e..000000000 --- a/.changeset/fix-jsonschema-postprocess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix: flatten simple allOf constraints in JSON Schema output for better codegen compatibility diff --git a/.changeset/fix-onedsl-r-inference.md b/.changeset/fix-onedsl-r-inference.md deleted file mode 100644 index 5077b1cb7..000000000 --- a/.changeset/fix-onedsl-r-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `R` inference in `OneDSL`, `OneDSLExt.modify` and `OneDSLExt.update`. The callback's effect type was annotated as `Effect<…, E, FixEnv>`, which deadlocked inference of `R` (TS6 fell back to `never`, tsgo to `unknown`, leaking `unknown` into yielded effects in generator handlers). Now matches the existing `AllDSLExt` pattern: bare `R` in the callback, `FixEnv` only in the return type. diff --git a/.changeset/fix-pascalcase-barrel.md b/.changeset/fix-pascalcase-barrel.md deleted file mode 100644 index f1b18d187..000000000 --- a/.changeset/fix-pascalcase-barrel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Fix `toCamelCase`/`toPascalCase` in barrel preset to preserve case boundaries. Mixed-case input like `AnCamelCase` was collapsed to `Ancamelcase` because the word splitter only matched contiguous alphanumerics. Now splits on case transitions (matching prior `lodash.camelCase` + `startCase` behavior). diff --git a/.changeset/fix-path-recursive-index-signature.md b/.changeset/fix-path-recursive-index-signature.md deleted file mode 100644 index 52372a3d1..000000000 --- a/.changeset/fix-path-recursive-index-signature.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch ---- - -Fix `TS2589: Type instantiation is excessively deep and possibly infinite` when querying models that embed `Schema.Defect()` (or any field encoded as `Json`). - -The query path type `Path` (used by `Q.where`/`Q.and`/`Q.order` to type dotted field paths) recursed through object types with no termination for self-referential ones. Under effect `4.0.0-beta.83`, `Schema.Defect()` encodes as `Json`, whose `JsonObject = { readonly [x: string]: Json }` index signature made `Path` descend forever, blowing past TypeScript's instantiation limit on any model with such a field (e.g. carrier-error states carrying `raw: Schema.Defect()`). - -`Path` now threads a `Seen` set of already-entered object types and stops before re-entering one, so self-referential types terminate (the field itself stays a valid leaf path; only the unbounded descent is cut). Finite models are unaffected. diff --git a/.changeset/fix-project-encodekeys-selection.md b/.changeset/fix-project-encodekeys-selection.md deleted file mode 100644 index 9ed42dc71..000000000 --- a/.changeset/fix-project-encodekeys-selection.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `Q.project` field selection with `Schema.encodeKeys` by deriving selected fields from the encoded schema shape. diff --git a/.changeset/fix-projection-union-adapter.md b/.changeset/fix-projection-union-adapter.md deleted file mode 100644 index 176b7e76c..000000000 --- a/.changeset/fix-projection-union-adapter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Fix projection schema typing for union members and computed projections. diff --git a/.changeset/fix-query-defaultvalues-double-encode.md b/.changeset/fix-query-defaultvalues-double-encode.md deleted file mode 100644 index c13939bed..000000000 --- a/.changeset/fix-query-defaultvalues-double-encode.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Stop re-encoding store `defaultValues` in Cosmos and SQL query builders. - -Stores already JSON-lower `defaultValues` at construction. A second schema encode of an ISO `Date` string throws `Expected a valid Date`, which failed `filter({ select: [id] })` even when the select path never decoded a document. diff --git a/.changeset/fix-repo-transformed-id-union.md b/.changeset/fix-repo-transformed-id-union.md deleted file mode 100644 index d627fa1ee..000000000 --- a/.changeset/fix-repo-transformed-id-union.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix repository `find` with transformed id fields in tagged union schemas diff --git a/.changeset/fix-repo-transformed-id.md b/.changeset/fix-repo-transformed-id.md deleted file mode 100644 index e706af004..000000000 --- a/.changeset/fix-repo-transformed-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix repository `find` with transformed id fields (e.g. composite ids using `decodeTo`) diff --git a/.changeset/fix-resolver-cache-precheck.md b/.changeset/fix-resolver-cache-precheck.md deleted file mode 100644 index 8782fcafe..000000000 --- a/.changeset/fix-resolver-cache-precheck.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix `withRequestResolverCache` causing "RequestResolver did not complete request" errors by using the `preCheck` hook instead of handling cache in `runAll`. Cache hits for in-flight or completed requests are now handled before entries enter the batch, preventing uncompleted entries. diff --git a/.changeset/fix-schema-interface-widening.md b/.changeset/fix-schema-interface-widening.md deleted file mode 100644 index c0a3a764e..000000000 --- a/.changeset/fix-schema-interface-widening.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect-app": patch ---- - -Fix schema interface widening regression introduced in 4.0.0-beta.255 (changeset `olive-onions-lose`). - -The original refactor annotated branded schema constants with interfaces extending `S.Codec & WithDefaults<...>`. That widened the underlying `BrandedSchema<...>` chain and dropped phantom slots (`Iso`, `~type.make.in`, `~type.parameters`, `Rebuild`, etc.) that downstream combinators read. In consumer projects this surfaced as `DecodingServices` / `EncodingServices` leaking as `unknown` — for example `Q.project(schema, "project")` failing with `Type 'unknown' is not assignable to type 'never'`, and RPC handler / generator-return shape mismatches against `Effect`. - -Each `*Schema` interface now extends the concrete underlying chain (e.g. `BrandedSchema`) and adds a call signature for `withDefaultMake` (and an explicit `withConstructorDefault` field for the numeric and `StringId` schemas). The runtime values are unchanged and the type-display improvement is preserved. - -Affected: `NonEmptyString*`, `Min3String255`, `StringId`, `Url`, `PositiveInt`, `NonNegativeInt`, `Int`, `PositiveNumber`, `NonNegativeNumber`, and `brandedStringId`. diff --git a/.changeset/fix-seed-marker-and-circular-dep.md b/.changeset/fix-seed-marker-and-circular-dep.md deleted file mode 100644 index b08aa48ed..000000000 --- a/.changeset/fix-seed-marker-and-circular-dep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix store seeding: break circular dependency where bulkSet re-entered seedNamespace, and add explicit seed markers to SQL/Pg stores using a dedicated `__seed__` namespace. diff --git a/.changeset/fix-sql-array-relation-queries.md b/.changeset/fix-sql-array-relation-queries.md deleted file mode 100644 index b0037ae9e..000000000 --- a/.changeset/fix-sql-array-relation-queries.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix SQL `whereSome`/`whereEvery` array relation queries using `EXISTS` with `json_each` (SQLite) / `jsonb_array_elements` (Pg). diff --git a/.changeset/fix-sql-in-null-handling.md b/.changeset/fix-sql-in-null-handling.md deleted file mode 100644 index b675415b2..000000000 --- a/.changeset/fix-sql-in-null-handling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix SQL `IN`/`NOT IN` with null values to use `IS NULL`/`IS NOT NULL` instead of `IN (NULL, ...)`. diff --git a/.changeset/fix-sql-includes-serialize.md b/.changeset/fix-sql-includes-serialize.md deleted file mode 100644 index 8e819d336..000000000 --- a/.changeset/fix-sql-includes-serialize.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix SQL includes-any/all double-quoting values for SQLite (JSON.stringify only needed for Postgres jsonb) diff --git a/.changeset/fix-sql-json-from-string-codec.md b/.changeset/fix-sql-json-from-string-codec.md deleted file mode 100644 index afb147746..000000000 --- a/.changeset/fix-sql-json-from-string-codec.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Apply `toCodecJson` in `SQLModel.JsonFromString` so SQL JSON string fields use the proper JSON-safe codec at the encode/decode boundary (aligns with queue, event, and gist call sites). diff --git a/.changeset/fix-sql-namespace-params.md b/.changeset/fix-sql-namespace-params.md deleted file mode 100644 index fa2666aeb..000000000 --- a/.changeset/fix-sql-namespace-params.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix SQLite store namespace parameter ordering in filter queries. The `_namespace` placeholder was prepended to the WHERE clause but its value was appended to the end of the positional params array, causing it to bind to the wrong placeholder. PostgreSQL was unaffected (uses indexed `$N` placeholders). diff --git a/.changeset/fix-sql-pg-seed-migrations-table.md b/.changeset/fix-sql-pg-seed-migrations-table.md deleted file mode 100644 index 9228c96e7..000000000 --- a/.changeset/fix-sql-pg-seed-migrations-table.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -SQL and Pg stores: use separate `_migrations` table for seed tracking instead of inserting a marker row into the data table, preventing it from appearing in `all`/`filter` queries. diff --git a/.changeset/fix-sql-select-etag-column.md b/.changeset/fix-sql-select-etag-column.md deleted file mode 100644 index 0d1d4e4aa..000000000 --- a/.changeset/fix-sql-select-etag-column.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix SQL select queries to read `_etag` from column instead of JSON data, preventing INSERT on update. diff --git a/.changeset/fix-sql-select-types-and-defaults.md b/.changeset/fix-sql-select-types-and-defaults.md deleted file mode 100644 index 2ccd01639..000000000 --- a/.changeset/fix-sql-select-types-and-defaults.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix SQLite select query type coercion using `json_quote` and apply defaultValues in SQL WHERE clauses via `COALESCE`. diff --git a/.changeset/fix-sql-whereevery-flip.md b/.changeset/fix-sql-whereevery-flip.md deleted file mode 100644 index 93e38a69e..000000000 --- a/.changeset/fix-sql-whereevery-flip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix SQL whereEvery double-negation bug causing wrong query when operators like notIn are used (especially with empty arrays) diff --git a/.changeset/fix-sqlite-per-ns-seed-migrations-table.md b/.changeset/fix-sqlite-per-ns-seed-migrations-table.md deleted file mode 100644 index 932a44bdd..000000000 --- a/.changeset/fix-sqlite-per-ns-seed-migrations-table.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -SQLite per-namespace adapter: use separate `_migrations` table for seed tracking instead of inserting a marker row into the data table, preventing it from appearing in `all`/`filter` queries. diff --git a/.changeset/fix-transformed-struct-defaults.md b/.changeset/fix-transformed-struct-defaults.md deleted file mode 100644 index 9d2139ed4..000000000 --- a/.changeset/fix-transformed-struct-defaults.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fix defaultsValueFromSchema for transformed struct schemas (e.g. decodeTo) in unions. Walk the AST properly instead of relying on schema-level .fields/.from chains. diff --git a/.changeset/fix-vue-form-numeric-type-detection.md b/.changeset/fix-vue-form-numeric-type-detection.md deleted file mode 100644 index 04696200a..000000000 --- a/.changeset/fix-vue-form-numeric-type-detection.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix numeric field type detection in `getMetadataFromSchema` for Effect v4 JSON schema output. `S.Number` now emits `anyOf` instead of a top-level `type: "number"`, causing fields to fall back to `"text"`. Detection now recurses through `anyOf`/`oneOf`/`allOf` to find the underlying numeric type. diff --git a/.changeset/fix-vue-interrupt-tests.md b/.changeset/fix-vue-interrupt-tests.md deleted file mode 100644 index e08911de1..000000000 --- a/.changeset/fix-vue-interrupt-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix interrupt repro tests to observe expected suspense interruption rejections. diff --git a/.changeset/fix-vue-lint-typescript-version.md b/.changeset/fix-vue-lint-typescript-version.md deleted file mode 100644 index 67cc08f64..000000000 --- a/.changeset/fix-vue-lint-typescript-version.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Fix lint: use typescript ~6.0.3 instead of native-preview for ESLint compatibility; keep tsgo for compilation diff --git a/.changeset/flat-turkeys-invent.md b/.changeset/flat-turkeys-invent.md deleted file mode 100644 index 04b3f39ee..000000000 --- a/.changeset/flat-turkeys-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Limit `client.method.mutate` extensions to only expose the mutation call and `wrap`. diff --git a/.changeset/flatten-nested-anyof.md b/.changeset/flatten-nested-anyof.md deleted file mode 100644 index 6020098d5..000000000 --- a/.changeset/flatten-nested-anyof.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Add nested anyOf flattening to JSON Schema post-processing diff --git a/.changeset/forty-bottles-tease.md b/.changeset/forty-bottles-tease.md deleted file mode 100644 index 038076323..000000000 --- a/.changeset/forty-bottles-tease.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -fix: restore context map container behavior diff --git a/.changeset/frank-ends-switch.md b/.changeset/frank-ends-switch.md deleted file mode 100644 index dd94d003f..000000000 --- a/.changeset/frank-ends-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -bump diff --git a/.changeset/frank-memes-sin.md b/.changeset/frank-memes-sin.md deleted file mode 100644 index ca2bf45ac..000000000 --- a/.changeset/frank-memes-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix sqlite diff --git a/.changeset/frank-papayas-design.md b/.changeset/frank-papayas-design.md deleted file mode 100644 index 55b4f6066..000000000 --- a/.changeset/frank-papayas-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -cleanup auth diff --git a/.changeset/fresh-buckets-pump.md b/.changeset/fresh-buckets-pump.md deleted file mode 100644 index 097c4a6b7..000000000 --- a/.changeset/fresh-buckets-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix TaggedUnion diff --git a/.changeset/fresh-candles-shake.md b/.changeset/fresh-candles-shake.md deleted file mode 100644 index 8db9c8e07..000000000 --- a/.changeset/fresh-candles-shake.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update packages diff --git a/.changeset/fresh-indexed-fields.md b/.changeset/fresh-indexed-fields.md deleted file mode 100644 index 5c76d47db..000000000 --- a/.changeset/fresh-indexed-fields.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Render indexed OmegaForm inputs outside form.Array and avoid duplicate error entries for registered array fields. diff --git a/.changeset/fresh-mails-visit.md b/.changeset/fresh-mails-visit.md deleted file mode 100644 index 235d6a843..000000000 --- a/.changeset/fresh-mails-visit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/vue": patch ---- - -Move `makeQueryKey` into `effect-app/client` and update Vue source and tests to import it from the shared client module. Vue still re-exports `makeQueryKey` from `src/lib` for compatibility. diff --git a/.changeset/fresh-paths-say.md b/.changeset/fresh-paths-say.md deleted file mode 100644 index 5d9b5c37e..000000000 --- a/.changeset/fresh-paths-say.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -fix atom invalidation diff --git a/.changeset/fresh-pianos-argue.md b/.changeset/fresh-pianos-argue.md deleted file mode 100644 index 8e81e76b5..000000000 --- a/.changeset/fresh-pianos-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Require `clientFor` invalidation resources when any command in the client configures them. diff --git a/.changeset/friendly-points-drum.md b/.changeset/friendly-points-drum.md deleted file mode 100644 index 42d52d888..000000000 --- a/.changeset/friendly-points-drum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Tighten schema helper generics in `Schema/ext.ts` so default-related helpers accept properly named schema type parameters instead of bare `S.Top` inputs. diff --git a/.changeset/fruity-meteors-fix.md b/.changeset/fruity-meteors-fix.md deleted file mode 100644 index 8db9c8e07..000000000 --- a/.changeset/fruity-meteors-fix.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update packages diff --git a/.changeset/full-oranges-heal.md b/.changeset/full-oranges-heal.md deleted file mode 100644 index c9099a6bf..000000000 --- a/.changeset/full-oranges-heal.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -remove double prefix diff --git a/.changeset/full-pugs-fall.md b/.changeset/full-pugs-fall.md deleted file mode 100644 index 2aba102b2..000000000 --- a/.changeset/full-pugs-fall.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -wrap vuetify reset diff --git a/.changeset/funky-guests-switch.md b/.changeset/funky-guests-switch.md deleted file mode 100644 index 2cb2e585e..000000000 --- a/.changeset/funky-guests-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -also make runSync version available\ diff --git a/.changeset/funny-webs-clap.md b/.changeset/funny-webs-clap.md deleted file mode 100644 index c97a7abdb..000000000 --- a/.changeset/funny-webs-clap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -add trace id and span id to toasts diff --git a/.changeset/fuzzy-eels-decide.md b/.changeset/fuzzy-eels-decide.md deleted file mode 100644 index e3af5ced7..000000000 --- a/.changeset/fuzzy-eels-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -cleanup diff --git a/.changeset/fuzzy-eggs-jump.md b/.changeset/fuzzy-eggs-jump.md deleted file mode 100644 index bbd529301..000000000 --- a/.changeset/fuzzy-eggs-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Migrate `Schema/ext` context helpers to Effect v4 by implementing `provide` and `contextFromServices` with proper service subtraction across decoding and encoding requirements. diff --git a/.changeset/gentle-bats-leave.md b/.changeset/gentle-bats-leave.md deleted file mode 100644 index b1cb0696c..000000000 --- a/.changeset/gentle-bats-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -fix missing fn diff --git a/.changeset/gentle-donkeys-tease.md b/.changeset/gentle-donkeys-tease.md deleted file mode 100644 index e27d75735..000000000 --- a/.changeset/gentle-donkeys-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix circular diff --git a/.changeset/gold-ghosts-laugh.md b/.changeset/gold-ghosts-laugh.md deleted file mode 100644 index 3f5762383..000000000 --- a/.changeset/gold-ghosts-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix: TaggedRequest should be an Opaque Schema Class diff --git a/.changeset/gold-wolves-shake.md b/.changeset/gold-wolves-shake.md deleted file mode 100644 index 6dfde3209..000000000 --- a/.changeset/gold-wolves-shake.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -use Finite instead of Number diff --git a/.changeset/green-coins-talk.md b/.changeset/green-coins-talk.md deleted file mode 100644 index 7133303b8..000000000 --- a/.changeset/green-coins-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Replace `ForceVoid` in `makeClient` with a typed codec transformation instead of an unsafe cast. diff --git a/.changeset/green-frogs-check.md b/.changeset/green-frogs-check.md deleted file mode 100644 index be2f966d7..000000000 --- a/.changeset/green-frogs-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Fix request handler input classification to use request schema fields instead of `make` parameters, preventing defaulted/nullable input fields from being treated as no-input handlers. diff --git a/.changeset/green-snakes-poke.md b/.changeset/green-snakes-poke.md deleted file mode 100644 index e4b9c8ed2..000000000 --- a/.changeset/green-snakes-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -pin dprint/formatter diff --git a/.changeset/green-taxis-project-hash.md b/.changeset/green-taxis-project-hash.md deleted file mode 100644 index 7dd2bc0d8..000000000 --- a/.changeset/green-taxis-project-hash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -feat: add client projection diff --git a/.changeset/green-taxis-rule.md b/.changeset/green-taxis-rule.md deleted file mode 100644 index 462fe15b4..000000000 --- a/.changeset/green-taxis-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add optional batching overloads for repository `save` and `remove` helpers. diff --git a/.changeset/handle-mutate-request-always-functions.md b/.changeset/handle-mutate-request-always-functions.md deleted file mode 100644 index 01ee4dd2d..000000000 --- a/.changeset/handle-mutate-request-always-functions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -`handle`, `mutate`, and `request` are now always functions, never a raw Effect or Stream. For no-input handlers the first argument is omitted (`handle()`, `request()`, `mutate()`). diff --git a/.changeset/handler-input-make-aligned.md b/.changeset/handler-input-make-aligned.md deleted file mode 100644 index 520782a0a..000000000 --- a/.changeset/handler-input-make-aligned.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Align request handler input typing with the request's `make` signature. Handlers are now classified as no-input only when the request schema declares no payload fields; any payload (even fully-optional) yields a function handler whose input matches `make`'s first parameter. Adds `HandlerInput` and threads it through `CommandFromRequest`. diff --git a/.changeset/heavy-bears-appear.md b/.changeset/heavy-bears-appear.md deleted file mode 100644 index 0af4b69a7..000000000 --- a/.changeset/heavy-bears-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -refactor(vue): remove 1s waiting toast delay in withDefaultToastStream diff --git a/.changeset/heavy-ducks-show.md b/.changeset/heavy-ducks-show.md deleted file mode 100644 index 5a8136582..000000000 --- a/.changeset/heavy-ducks-show.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add `relation(...).length()` computed projection — emits `ARRAY_LENGTH` on Cosmos / `arrayLength` on SQL / native `.length` on Memory. Cheaper than `relation.count()` for unconditional array sizing (no subquery scan, just metadata read). - -```ts -projectComputed( - S.Struct({ id: S.String, packageCount: S.NonNegativeInt }), - computed({ packageCount: relation("packages").length() }) -) -``` diff --git a/.changeset/honest-planets-fry.md b/.changeset/honest-planets-fry.md deleted file mode 100644 index 58b6fe3c3..000000000 --- a/.changeset/honest-planets-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": minor ---- - -feat: add native fork diff --git a/.changeset/http-span-name.md b/.changeset/http-span-name.md deleted file mode 100644 index 7fa8bd871..000000000 --- a/.changeset/http-span-name.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -RequestContextMiddleware: set request name to `HTTP ` (strip query string) for clearer span/trace names. diff --git a/.changeset/huge-carrots-deny.md b/.changeset/huge-carrots-deny.md deleted file mode 100644 index d2614cd19..000000000 --- a/.changeset/huge-carrots-deny.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -update to latest effect beta diff --git a/.changeset/humble-walls-fail.md b/.changeset/humble-walls-fail.md deleted file mode 100644 index 3327be42f..000000000 --- a/.changeset/humble-walls-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix DatabaseError typing diff --git a/.changeset/icy-paws-return.md b/.changeset/icy-paws-return.md deleted file mode 100644 index 955ba2afb..000000000 --- a/.changeset/icy-paws-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -bogus diff --git a/.changeset/indefinite-waiting-toast.md b/.changeset/indefinite-waiting-toast.md deleted file mode 100644 index b8698031d..000000000 --- a/.changeset/indefinite-waiting-toast.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -In-progress toasts (`withToast` waiting toast and `withDefaultToastStream` waiting/progress toasts) now persist indefinitely (`timeout: Infinity`) until replaced by the success/failure toast or dismissed. Previously they used the underlying toast adapter's default duration and could disappear before the operation finished. diff --git a/.changeset/infra-generate-from-schema-seed.md b/.changeset/infra-generate-from-schema-seed.md deleted file mode 100644 index d1554ac36..000000000 --- a/.changeset/infra-generate-from-schema-seed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -`generateFromSchema` advances its seed on every call, so successive samples differ (deterministic across runs) instead of always returning the same value. diff --git a/.changeset/infra-prefer-fn-untraced.md b/.changeset/infra-prefer-fn-untraced.md deleted file mode 100644 index 9a19c62ba..000000000 --- a/.changeset/infra-prefer-fn-untraced.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Replace `(...) => Effect.gen` with `Effect.fnUntraced` and convert select multi-step `pipe` chains to `Effect.gen` across infra. diff --git a/.changeset/infra-sentry-node-dep.md b/.changeset/infra-sentry-node-dep.md deleted file mode 100644 index 30f66e1f3..000000000 --- a/.changeset/infra-sentry-node-dep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Declare `@sentry/node` as a runtime dependency of `@effect-app/infra`. `errorReporter.ts` imports it statically, so `pnpm install --prod` of linked source (Docker) must install it next to the package, not only as a peer of the app. diff --git a/.changeset/invalidation-set-add-shortcut-and-batch.md b/.changeset/invalidation-set-add-shortcut-and-batch.md deleted file mode 100644 index 2ed5e26f4..000000000 --- a/.changeset/invalidation-set-add-shortcut-and-batch.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect-app": patch ---- - -`InvalidationSet.add` accepts arrays and is exposed as a static shortcut. - -- Single item: `yield* InvalidationSet.add(UserRsc.GetMe)` -- Batch: `yield* InvalidationSet.add([UserRsc.GetMe, ["custom", "key"]])` -- Skips the `.use(_ => _.add(...))` boilerplate. - -Existing `InvalidationSet.use(_ => _.add(...))` form still works; the service -identity is preserved via `Object.assign` so `Effect.provideService` and -`.use` / `.useSync` continue to operate on the same `Context.Reference`. diff --git a/.changeset/itchy-mangos-find.md b/.changeset/itchy-mangos-find.md deleted file mode 100644 index 6c80d9d0b..000000000 --- a/.changeset/itchy-mangos-find.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix withDefault types diff --git a/.changeset/jitm-json-store-boundary.md b/.changeset/jitm-json-store-boundary.md deleted file mode 100644 index 0ee3135cf..000000000 --- a/.changeset/jitm-json-store-boundary.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -`jitM` runs at the store boundary as JSON → JSON, before the `toCodecJson(toEncoded(schema))` decode; repositories decode the Encoded shape only. - -`jitM` used to be applied by the repository _after_ the store had already decoded the document, and was typed `(pm: Encoded) => Encoded` - a lie, since jitMs are written against the stored JSON and `Encoded` holds native `Date`/`Map`/`Set` values. - -The read pipeline is now: raw JSON document → merge `defaultValues` (unchanged: they only fill _absent_ keys, so an explicitly stored `null` reaches `jitM`) → `jitM` (JSON → JSON) → decode `Schema.toCodecJson(Schema.toEncoded(schema))` → Encoded. The repository then decodes Encoded → the domain type with `schema` and no longer applies `jitM` at all. Because `jitM` now runs before any schema decode sees the document, it can repair legacy shapes including explicit `null`s. - -- `StoreConfig.jitM?: (json: JsonRecord) => JsonRecord` is new, where `JsonRecord` (exported from `effect-app/Store`) is `{ readonly [key: string]: Schema.Json }`. It is not applied on the write/encode path, and never receives `_etag`. -- `RepositoryOptions.jitM` changes type from `(pm: Encoded) => Encoded` to `(json: JsonRecord) => JsonRecord` and is forwarded into the store config. -- **Migrating a jitM:** it now receives and must return JSON. A `Date` field arrives as an ISO string (return a string, not a `Date`), a `ReadonlySet` as an array, a `ReadonlyMap` as an array of `[key, value]` pairs. Field access is index-signature based (`json["x"]`) rather than typed property access. -- The JSON→Encoded decode stays strict: a document `jitM` does not repair fails at the store boundary instead of being read back half-decoded. -- `ValidationError.jitMResult` is deprecated: `validateSample` only ever sees the store's output, so it is identical to `rawData`. diff --git a/.changeset/khaki-frogs-wave.md b/.changeset/khaki-frogs-wave.md deleted file mode 100644 index 78412a8ac..000000000 --- a/.changeset/khaki-frogs-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -omegaform: validate all fields on submit before delegating to tanstack handleSubmit, ensuring submit reveals all validation errors. diff --git a/.changeset/kind-lizards-bathe.md b/.changeset/kind-lizards-bathe.md deleted file mode 100644 index f7bbf607d..000000000 --- a/.changeset/kind-lizards-bathe.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update to latest effect diff --git a/.changeset/large-pugs-strive.md b/.changeset/large-pugs-strive.md deleted file mode 100644 index 6e62335da..000000000 --- a/.changeset/large-pugs-strive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -bump to release diff --git a/.changeset/late-forks-obey.md b/.changeset/late-forks-obey.md deleted file mode 100644 index 67d6b721f..000000000 --- a/.changeset/late-forks-obey.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/vue": minor ---- - -Add command invalidation helpers that preserve query-only resource types and pass mutation input and `Exit` results into invalidation callbacks. Update Vue `clientFor` to merge request-level invalidation config with call-site invalidation and require matching invalidation resources. diff --git a/.changeset/lazy-boats-lay.md b/.changeset/lazy-boats-lay.md deleted file mode 100644 index 5dfdac068..000000000 --- a/.changeset/lazy-boats-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix rpc middleware context type issue diff --git a/.changeset/lazy-model-facades.md b/.changeset/lazy-model-facades.md deleted file mode 100644 index 02a937c1b..000000000 --- a/.changeset/lazy-model-facades.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/eslint-codegen-model": patch ---- - -Add generated opaque model facades that expose static Type, Encoded, Make, and Schema declarations without leaking the private struct schema type to downstream project references. diff --git a/.changeset/legal-weeks-raise.md b/.changeset/legal-weeks-raise.md deleted file mode 100644 index 29be81175..000000000 --- a/.changeset/legal-weeks-raise.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -adapt isObject change diff --git a/.changeset/lemon-emus-behave.md b/.changeset/lemon-emus-behave.md deleted file mode 100644 index 8181569d9..000000000 --- a/.changeset/lemon-emus-behave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -bye cruft diff --git a/.changeset/little-berries-sip.md b/.changeset/little-berries-sip.md deleted file mode 100644 index 3bdf51b4e..000000000 --- a/.changeset/little-berries-sip.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update to effect beta 43 diff --git a/.changeset/little-planets-bake.md b/.changeset/little-planets-bake.md deleted file mode 100644 index 8ab5b6b64..000000000 --- a/.changeset/little-planets-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": minor ---- - -Restore OmegaForm input reactivity broken by the @tanstack/vue-form 1.32 bump — the Field slot's state prop became a stale snapshot, so bind inputs to the reactive field.state instead. diff --git a/.changeset/live-query-dependencies.md b/.changeset/live-query-dependencies.md deleted file mode 100644 index d744cdb24..000000000 --- a/.changeset/live-query-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add query-owned live invalidation with recorded dependency filtering, connect-before-fetch coordination, race buffering, and configurable client-side coalescing. diff --git a/.changeset/long-regions-change.md b/.changeset/long-regions-change.md deleted file mode 100644 index 7381132f9..000000000 --- a/.changeset/long-regions-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -again diff --git a/.changeset/loud-planets-cheer.md b/.changeset/loud-planets-cheer.md deleted file mode 100644 index bc386da77..000000000 --- a/.changeset/loud-planets-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -expose Input diff --git a/.changeset/lucky-papayas-fetch.md b/.changeset/lucky-papayas-fetch.md deleted file mode 100644 index 83e96336d..000000000 --- a/.changeset/lucky-papayas-fetch.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- diff --git a/.changeset/lucky-streets-add.md b/.changeset/lucky-streets-add.md deleted file mode 100644 index 8e5ca642b..000000000 --- a/.changeset/lucky-streets-add.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -the return of `Context` diff --git a/.changeset/lucky-zebras-invent.md b/.changeset/lucky-zebras-invent.md deleted file mode 100644 index 73f8bc979..000000000 --- a/.changeset/lucky-zebras-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Add a `Commander` error-renderer registry via `Context.Reference` and apply registered guarded renderers before default error formatting. diff --git a/.changeset/major-drinks-stop.md b/.changeset/major-drinks-stop.md deleted file mode 100644 index 112305dba..000000000 --- a/.changeset/major-drinks-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix memory computation diff --git a/.changeset/major-planets-lie.md b/.changeset/major-planets-lie.md deleted file mode 100644 index 887213b27..000000000 --- a/.changeset/major-planets-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -run codegen model from src locally diff --git a/.changeset/many-ideas-report.md b/.changeset/many-ideas-report.md deleted file mode 100644 index 1f6f370f1..000000000 --- a/.changeset/many-ideas-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -update oxlint rule diff --git a/.changeset/meta-codegen-no-export.md b/.changeset/meta-codegen-no-export.md deleted file mode 100644 index 1ccc7f58a..000000000 --- a/.changeset/meta-codegen-no-export.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Don't export `Req` in meta codegen preset diff --git a/.changeset/meta-module-name-override.md b/.changeset/meta-module-name-override.md deleted file mode 100644 index 2451ff248..000000000 --- a/.changeset/meta-module-name-override.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch ---- - -Add `stripSuffixes` option to the meta codegen preset and configure `.Queries` stripping in the shared oxlint config. diff --git a/.changeset/metal-mammals-smell.md b/.changeset/metal-mammals-smell.md deleted file mode 100644 index 0a417e259..000000000 --- a/.changeset/metal-mammals-smell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/vue-components": patch -"@effect-app/vue": patch ---- - -move out Commander and friends from experimental diff --git a/.changeset/middleware-error-from-dynamic-rcm.md b/.changeset/middleware-error-from-dynamic-rcm.md deleted file mode 100644 index 26a1e5a67..000000000 --- a/.changeset/middleware-error-from-dynamic-rcm.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -`MiddlewareMaker.makeMiddlewareBasic` now derives each middleware's effective error from both the static `error` field on the tag AND the `rcm` config entry referenced by `dynamic.key`, rather than relying on the static field alone. - -Middlewares declared with `dynamic: RequestContextMap.get("foo")` (instead of an explicit static `error: ...`) end up with `tag.error = Schema.Never` at runtime — `RpcMiddleware.Tag` defaults the static error to `Never` when not provided. The composite `MiddlewareMaker.Tag(...).middleware(...)` walked `make[*].error` to build its own error union, collapsing to `Union ≡ Never`. - -`Rpc.exitSchema` walks `rpc.middlewares[*].error` when building the wire-level failure union for every rpc kind. Empty-union meant middleware-thrown errors (`NotLoggedInError`, `UnauthorizedError`, etc.) never reached the wire schema. Query/command happened to work because their wire `errorSchema = resource.error` already covered the merge from `makeRpcClient`. Stream rpcs have `errorSchema` force-set to `Schema.Never` by effect-rpc, so the resource-level merge never reached the wire — middleware errors decoded as "Expected never, got X". - -Per middleware, the new logic pushes both the static `_.error` (if non-`Never`) and `rcm[_.dynamic.key].error` (if non-`Never`) into the composite's failure union. diff --git a/.changeset/middleware-errors-via-rpc-tag.md b/.changeset/middleware-errors-via-rpc-tag.md deleted file mode 100644 index 8bcb52430..000000000 --- a/.changeset/middleware-errors-via-rpc-tag.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Source middleware errors exclusively from the rpc middleware tag, and move command/stream invalidation wrap/unwrap entirely into the routing layer (server) and `apiClientFactory` (client). `InvalidationMiddleware` and `InvalidationMiddlewareLive` are removed. - -### Resource error schemas - -Three sites that used to fold `RequestContextMap[*].error` into a request's own error schema now stop doing so: - -- `makeRpcClient` / `makeRequestClass` — `failureSchema` is just `config.error` (still merged with the optional `generalErrors` parameter, which is the only remaining error mix on both type and runtime levels). -- `MiddlewareMaker.rpc()` — `error: options.error` only; the previous union with `rcm.config[*].error` is gone. -- Routing and `apiClientFactory.makeRpcGroupFromRequestsAndModuleName` — `Invalidation.makeCommandRpc` is called with `error: resource.error` (no widening with the composite middleware error union). - -Middleware errors reach the client through the rpc's `middlewares[*].error` failure-union channel of `Rpc.exitSchema`, exposed by attaching the middleware tag to the rpc on both sides: - -- **Server**: `makeRouter(middleware)` attaches the live composite tag (existing behavior). -- **Client**: new `middleware` option on `ClientForOptions` / `ApiClientFactory.makeFor(layer, { middleware })` attaches the same tag schema-only (no Live invoked). Threaded through `makeRpcGroupFromRequestsAndModuleName` to `RpcGroup.middleware(tag)`. Without it, stream rpcs (whose top-level `errorSchema` is forced to `Never` by effect-rpc) hit `SchemaError: Expected never | { _tag: "error", ... }` decoding middleware-thrown errors that bypass the in-stream `Stream.catch` wrap. - -**Migration**: handlers that yield errors previously sourced from rcm (e.g. `yield* new UnauthorizedError()`) now require those errors to be declared explicitly on the resource — `Req.Query()("...", fields, { success, error: UnauthorizedError })`. The handler error type no longer auto-includes the rcm union. - -### Invalidation wrap/unwrap - -- `routing.ts` (server) provides a per-request `InvalidationSet` for commands, wraps the success value as `CommandResponseWithMetaData`, and converts handler-thrown failures into `CommandFailureWithMetaData` so accumulated invalidation keys reach the client on either path. Stream wrap (per-chunk envelope + final `done` chunk) was already in routing and is unchanged. -- `apiClientFactory.ts` (client) `unwrapCommand` strips both envelopes and forwards keys to `InvalidationKeysFromServer`. -- `InvalidationMiddleware` (the tag) and `InvalidationMiddlewareLive` (the layer) are **removed**. The middleware was the previous home of the wrap; with the wrap moved to routing/apiClientFactory, the middleware became a thin pass-through and is no longer needed. `DefaultGenericMiddlewares` and `DefaultGenericMiddlewaresLive` shrink accordingly — no migration needed for callers that used the defaults; callers that referenced `InvalidationMiddleware` / `InvalidationMiddlewareLive` directly should drop those imports. - -Middleware-thrown errors are never wrapped: by definition the handler never ran, so there is nothing to invalidate. They flow raw on the Cause and the client decodes them via the middleware-tag failure-union channel described above. diff --git a/.changeset/mighty-hoops-help.md b/.changeset/mighty-hoops-help.md deleted file mode 100644 index 3c2ac4870..000000000 --- a/.changeset/mighty-hoops-help.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -fix diff --git a/.changeset/migrate-tsgolint.md b/.changeset/migrate-tsgolint.md deleted file mode 100644 index 1ce372869..000000000 --- a/.changeset/migrate-tsgolint.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@effect-app/eslint-shared-config": minor -"effect-app": patch -"@effect-app/cli": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. diff --git a/.changeset/modern-humans-chew.md b/.changeset/modern-humans-chew.md deleted file mode 100644 index 8011b393c..000000000 --- a/.changeset/modern-humans-chew.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -dprint bs diff --git a/.changeset/moody-peas-smoke.md b/.changeset/moody-peas-smoke.md deleted file mode 100644 index 3c2ac4870..000000000 --- a/.changeset/moody-peas-smoke.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -fix diff --git a/.changeset/mutate-invalidation-shorthands.md b/.changeset/mutate-invalidation-shorthands.md deleted file mode 100644 index 68567cbff..000000000 --- a/.changeset/mutate-invalidation-shorthands.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"@effect-app/vue": patch -"effect-app": patch ---- - -`queryInvalidation` / `invalidatesQueries` accept shorthand entries (per-mutation, Command, client-level `QueryInvalidation` maps, and server-side `Req.Command` `invalidatesQueries` callbacks). - -Each entry returned may now be: - -- a raw query key (`string[]`) -- an RPC handler (`{ id, options? }`) — its query key is derived via `makeQueryKey` -- the existing `{ filters, options }` raw tanstack-query invalidation - -```ts -queryInvalidation: (queryKey) => [ - queryKey, - GetMe, - PackListIndex -] -``` - -equivalent to: - -```ts -queryInvalidation: (queryKey) => [ - { filters: { queryKey } }, - { filters: { queryKey: makeQueryKey(GetMe) } }, - { filters: { queryKey: makeQueryKey(PackListIndex) } } -] -``` diff --git a/.changeset/mutate-wrap-stream-no-args.md b/.changeset/mutate-wrap-stream-no-args.md deleted file mode 100644 index 550c0c103..000000000 --- a/.changeset/mutate-wrap-stream-no-args.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -`mutate.wrap` on stream handlers now works the same as on command/query handlers: it can be called without arguments or with only combinators, with the underlying stream handler pre-baked in. diff --git a/.changeset/namespaced-change-feed.md b/.changeset/namespaced-change-feed.md deleted file mode 100644 index 3bdfc4eb7..000000000 --- a/.changeset/namespaced-change-feed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Repository `changeFeed` is now namespace-aware. Events carry the `storeId` namespace as a third tuple element (`ChangeFeedEvent = [items, op, namespace]`). `subscribe` accepts `options.namespace` to register a per-namespace handler; omitting it registers a wildcard handler that receives events from every namespace. Per-namespace handler buckets eliminate cross-namespace fan-out and avoid waking handlers for irrelevant tenants. diff --git a/.changeset/native-encoded-query-json.md b/.changeset/native-encoded-query-json.md deleted file mode 100644 index e9c963a39..000000000 --- a/.changeset/native-encoded-query-json.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor -"@effect-app/vue-components": minor ---- - -Stop forcing Date/Map/Set Encoded shapes to JSON. - -`Schema.Date` / `ReadonlySet` / `ReadonlyMap` now keep native Encoded types (`Date`, `Set`, `Map`). Use `DateFromString`, `ReadonlySetFromArray`, and `ReadonlyMapFromArray` when the Encoded form must be JSON. The query DSL accepts those native values, including array ops (`includes` / `in` / `includes-any`) on `Date[]` and `ReadonlySet` fields. Memory, Disk, SQL, and Cosmos convert Encoded Date/Map/Set through `Schema.toCodecJson` on write/read; query parameters and defaults lower the same way from the store schema. App types such as DateOnly stay native Encoded and JSON-lower via that schema — not a type registry. diff --git a/.changeset/neat-brooms-wave.md b/.changeset/neat-brooms-wave.md deleted file mode 100644 index ef7abda56..000000000 --- a/.changeset/neat-brooms-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Ignore commented-out exported classes when detecting model schemas for generated `Encoded` namespaces. diff --git a/.changeset/neat-dingos-tease.md b/.changeset/neat-dingos-tease.md deleted file mode 100644 index 8d1870a93..000000000 --- a/.changeset/neat-dingos-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Align effect-app tagged union helpers with Effect v4 by delegating to the native tagged union utilities, exposing v4-style `cases` and `guards`, and preserving the local `tags` helper. diff --git a/.changeset/neat-pumpkins-juggle.md b/.changeset/neat-pumpkins-juggle.md deleted file mode 100644 index d3dbe4b32..000000000 --- a/.changeset/neat-pumpkins-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add computed projection expression sums with grouped and normalized variants for projectComputed relation aggregations. diff --git a/.changeset/new-badgers-wait.md b/.changeset/new-badgers-wait.md deleted file mode 100644 index a46a4fb53..000000000 --- a/.changeset/new-badgers-wait.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Update Effect dependencies to 4.0.0-beta.31 across workspace packages. diff --git a/.changeset/new-berries-march.md b/.changeset/new-berries-march.md deleted file mode 100644 index c2b5e4038..000000000 --- a/.changeset/new-berries-march.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"effect-app": patch -"@effect-app/infra": patch ---- - -unify encoded function for when you use encodedKeys diff --git a/.changeset/nice-jars-grow.md b/.changeset/nice-jars-grow.md deleted file mode 100644 index adab7c6be..000000000 --- a/.changeset/nice-jars-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Uses onBlur instead of intrusive onChange diff --git a/.changeset/nice-needles-film.md b/.changeset/nice-needles-film.md deleted file mode 100644 index 8692c6c82..000000000 --- a/.changeset/nice-needles-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -no need for .lock.lock diff --git a/.changeset/nice-shrimps-fold.md b/.changeset/nice-shrimps-fold.md deleted file mode 100644 index c49bca027..000000000 --- a/.changeset/nice-shrimps-fold.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Update request helper typing and runtime invocation to rely on schema `.make` instead of class constructors, avoiding `new`-based assumptions for request schemas. diff --git a/.changeset/ninety-stamps-end.md b/.changeset/ninety-stamps-end.md deleted file mode 100644 index e39ea913e..000000000 --- a/.changeset/ninety-stamps-end.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -bogus diff --git a/.changeset/no-await-effect.md b/.changeset/no-await-effect.md deleted file mode 100644 index e99b97e64..000000000 --- a/.changeset/no-await-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": minor ---- - -Add `no-await-effect` lint rule that errors when awaiting Effect, Option, Result, or Fiber values from the effect library. Enabled when type-checked linting is active (`ESLINT_TS=1`). diff --git a/.changeset/number-input-dual-separator.md b/.changeset/number-input-dual-separator.md deleted file mode 100644 index 336fd94f6..000000000 --- a/.changeset/number-input-dual-separator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm number fields accept both "." and "," while typing: the wrong separator is translated to the active one (locale or explicit decimal-separator). Int fields no longer silently block decimals (precision null): invalid values go through and the schema error shows. diff --git a/.changeset/odd-deers-work.md b/.changeset/odd-deers-work.md deleted file mode 100644 index bd19a3451..000000000 --- a/.changeset/odd-deers-work.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch ---- - -Change `Schema.Class` and `Schema.TaggedClass` wrappers to default constructor options to `{ disableValidation: true }`. - -This avoids strict class identifier validation by default when constructing wrapper classes (for example passing a compatible view class), while keeping existing behavior when explicit options are provided. diff --git a/.changeset/odd-tigers-scope.md b/.changeset/odd-tigers-scope.md deleted file mode 100644 index 30fbd5abd..000000000 --- a/.changeset/odd-tigers-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -SQL and Pg stores now scope seed migration records by namespace and table name in shared databases to avoid cross-namespace seed collisions. diff --git a/.changeset/old-snakes-film.md b/.changeset/old-snakes-film.md deleted file mode 100644 index 4fedea556..000000000 --- a/.changeset/old-snakes-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -able to configure schema concurrency diff --git a/.changeset/old-suns-kneel.md b/.changeset/old-suns-kneel.md deleted file mode 100644 index b7d6bcb98..000000000 --- a/.changeset/old-suns-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix type issue? diff --git a/.changeset/olive-onions-lose.md b/.changeset/olive-onions-lose.md deleted file mode 100644 index fb294dc9f..000000000 --- a/.changeset/olive-onions-lose.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch ---- - -Reduce schema type complexity by exposing explicit interfaces for branded string and number schemas. - -Each branded constant in `Schema/strings.ts`, `Schema/moreStrings.ts`, and `Schema/numbers.ts` now has a named interface (e.g. `NonEmptyStringSchema`, `StringIdSchema`, `PositiveIntSchema`, `UrlSchema`) annotating its `export const`. Mirrors the pattern used in `effect/Schema` itself — TypeScript reports the interface name instead of expanding the full pipe/brand/extension chain, which shrinks inferred types in consumer code and speeds up type display. - -No runtime or API surface changes; `brandedStringId` now returns `BrandedStringIdSchema` (same shape as before, just named). diff --git a/.changeset/omega-native-input-types.md b/.changeset/omega-native-input-types.md deleted file mode 100644 index 7f4cd02b0..000000000 --- a/.changeset/omega-native-input-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm: expose native input types `tel`, `url`, `color`, `time`, `search` (already supported by `getInputType`), and render an editable text input with a dev `console.warn` for any input type no renderer branch handles (e.g. an unrecognized `"unknown"` schema type or a custom `type`) instead of rendering nothing. diff --git a/.changeset/omega-nullable-nested-struct.md b/.changeset/omega-nullable-nested-struct.md deleted file mode 100644 index 6b97fadbb..000000000 --- a/.changeset/omega-nullable-nested-struct.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm: deep-fill defaults for nullable nested structs. When a `S.NullOr(S.Struct(...))` field materialises because one child was filled, its untouched nullable siblings are now normalized to `null` (or their schema default) — in the live form state, and during validation and decoding — instead of being rejected as "field must not be empty". diff --git a/.changeset/omegaform-between-localization.md b/.changeset/omegaform-between-localization.md deleted file mode 100644 index 7bb347b0c..000000000 --- a/.changeset/omegaform-between-localization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Localize `S.isBetween` validation failures: the violated side now maps to the `validation.number.min` / `validation.number.max` messages instead of falling back to the English default formatter. Also fixes the inverted `isExclusive` flag on the min/max messages for `isGreaterThan[OrEqualTo]` / `isLessThan[OrEqualTo]` (inclusive checks now say "at least/at most", strict ones "greater/less than"). diff --git a/.changeset/omegaform-decodeto-meta-from-encoded.md b/.changeset/omegaform-decodeto-meta-from-encoded.md deleted file mode 100644 index fd495e003..000000000 --- a/.changeset/omegaform-decodeto-meta-from-encoded.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm: meta extraction now walks the encoded (source) side of struct-level `decodeTo` transformations, so the outer schema drives field-level validation. Example: `S.Struct({ amount: S.NonNegativeInt }).pipe(S.decodeTo(S.Struct({ amount: S.PositiveInt }), ...))` now lets users enter `0` without a field error and the decode fallback can rewrite it before submit. diff --git a/.changeset/omegaform-enter-submit-repro.md b/.changeset/omegaform-enter-submit-repro.md deleted file mode 100644 index 0467fe6f1..000000000 --- a/.changeset/omegaform-enter-submit-repro.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fix OmegaForm default inputs leaking the internal form object as a native `form="[object Object]"` attribute, restoring browser implicit Enter-key submission. Adds a Storybook repro for the default Vuetify input renderer. diff --git a/.changeset/omegaform-input-registry.md b/.changeset/omegaform-input-registry.md deleted file mode 100644 index d81105e39..000000000 --- a/.changeset/omegaform-input-registry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm: register custom input components per `type` via `omegaConfig.inputs`. Registered components receive the `OmegaRendererProps` contract (`{ inputProps, field, state }`) and a registered key makes `` type-valid. Resolution order: per-instance `#default` slot → `inputs[type]` → built-in renderer. `createUseFormWithCustomInput` also honors `omegaConfig.inputs`: per-`type` registrations override the universal custom input, with the same typed `type` inference. Also drops the redundant `validators` forwarding to the internal input. diff --git a/.changeset/omegaform-module-split.md b/.changeset/omegaform-module-split.md deleted file mode 100644 index 7fb1ef84f..000000000 --- a/.changeset/omegaform-module-split.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Refactor `OmegaForm` into focused modules and switch validation to a form-level `onDynamic` validator with `revalidateLogic()` so cross-field checks re-validate on every input change. `OmegaFormStuff.ts` is gone, split into `meta/{types,checks,createMeta,walker,defaults,redacted}.ts`, `validation/localized.ts`, `errors.ts`, `hocs.ts`, `inputs.ts`, `submit.ts`, `persistency.ts`, and `types.ts`; `useOmegaForm.ts` is orchestration-only. - -Restored localization parity for `S.Email` (matched via the refine's `identifier` annotation) and for `S.Literals` / `S.Array(S.Literals(...))` (via an AST pre-pass that stamps `validation.not_a_valid` so Effect's formatter — which bypasses both hooks for `AnyOf` issues — picks them up through `findMessage`). - -Behavior tweaks worth noting: per-field standard-schema validators are removed in favor of one form-level dynamic validator; `OmegaInput`'s `:key="fieldKey"` re-mount is gone; the post-change `errorMap.onSubmit` reset is gone (relies on TanStack revalidation); JSON-schema-derived annotations are no longer merged into `FieldMeta`. Public exports are preserved. diff --git a/.changeset/omegaform-typecheck-stories.md b/.changeset/omegaform-typecheck-stories.md deleted file mode 100644 index 2bbb52a03..000000000 --- a/.changeset/omegaform-typecheck-stories.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Typecheck the OmegaForm Storybook stories (previously outside `tsconfig` scope) and migrate them off removed/renamed Schema APIs surfaced by it: `.withDefault` → `.withConstructorDefault`, error-filter `{ path, message }` → `{ path, issue }`, dropped deprecated `overrideDefaultValues`, and an `OmegaAutoGenMeta` type argument. Internal only — no change to the published API. diff --git a/.changeset/omegaform-vnumber-input.md b/.changeset/omegaform-vnumber-input.md deleted file mode 100644 index 72e0ffa69..000000000 --- a/.changeset/omegaform-vnumber-input.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": minor ---- - -OmegaForm number fields now render Vuetify 4's `v-number-input` instead of `v-text-field type="number"` (`range` keeps its `v-slider`). Precision is schema-driven (`S.Int` → 0, plain numbers → free decimals), controls default to the stacked variant, and any `VNumberInput` prop (`precision`, `step`, `control-variant`, `decimal-separator`, ...) can be overridden per field via attrs. Schema `min`/`max` are exposed to assistive tech as spinbutton ARIA bounds but are deliberately not passed as component props, so out-of-range values keep reaching schema validation and show its localized error instead of being silently clamped. diff --git a/.changeset/optimize-codegen-barrel-model.md b/.changeset/optimize-codegen-barrel-model.md deleted file mode 100644 index e96a8ba0c..000000000 --- a/.changeset/optimize-codegen-barrel-model.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Optimize codegen barrel and model plugins: - -- Drop `io-ts-extra`, `io-ts`, and `lodash` dependencies; replace with native JS helpers -- Add fast string comparison (skip AST parse) for barrel and model equality checks -- Avoid triple file reads in the CLI path: pass already-read source into `model` via context -- Eliminate double read in `run()`: pass source string into `updateFile` -- Extract shared logic (`blockRe`, helpers, `renderPreset`) into `src/shared/codegen-block.ts` -- Remove dead `fs.existsSync`/`fs.statSync` checks in `model` preset -- Use `Set` for O(1) dedup in `model` preset -- Move `last` helper to module scope in `barrel` preset -- Pre-compile all regex constants at module scope; use fresh `RegExp` copy per oxlint `Program` visit to avoid shared `lastIndex` mutations diff --git a/.changeset/otel-semconv-spans.md b/.changeset/otel-semconv-spans.md deleted file mode 100644 index 9cbb915a5..000000000 --- a/.changeset/otel-semconv-spans.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Standardize span attributes on OpenTelemetry semantic conventions. - -All Store adapters (Cosmos, PostgreSQL, SQLite, Memory, Disk) and Queue adapters -(Service Bus, SQL, Memory) now emit OTel-compliant span names and attributes via -the new `@effect-app/infra/otel` helper module. - -Span name convention: ` ` (low cardinality). - -Attribute key migration: - -| Old | New | -| --------------------------------------------------- | --------------------------------------------------------------------------------- | -| `repository.table_name` / `repository.container_id` | `db.collection.name` | -| `repository.namespace` | `db.namespace` | -| `repository.model_name` / `itemType` | `app.entity` | -| `id` / `itemId` (entity span attr) | `app.entity.id` | -| `itemIds` | `app.entity.ids` | -| `db.cosmos.request_charge` | `azure.cosmosdb.operation.request_charge` | -| `db.cosmos.resource_count` | `db.response.returned_rows` | -| `db.cosmos.response_bytes` | `db.response.body.size` | -| `disk.file` / `disk.file_size` | `disk.file.path` / `disk.file.size` | -| `queue.name` | `messaging.destination.name` | -| `queue.sessionId` | `messaging.message.conversation_id` | -| `queue.type` | `messaging.system` | -| `queue.input` (full body) | `messaging.message.body` (+ `messaging.message.id`, `messaging.message.type`) | -| `message_tags` | `messaging.message.types` + `messaging.batch.message_count` | -| `request.name` | `code.function.name` (from `spanAttributes`) / `rpc.method` (middleware) | -| `request.locale` | `app.locale` | -| `request.namespace` | `app.tenant.id` | -| `request.source.id` | `client.id` | -| `request.user.sub` / `.roles` | `user.id` / `user.roles` | -| `requestInput` | `rpc.request.payload` | -| `connectionId` | `network.connection.id` | -| Span `Request..` | Span `/` + `rpc.system`/`rpc.service`/`rpc.method` (kind: server) | -| `.` (SQL/Model) | OTel db span via `withDbSpan`, with `dbSystem?` option | - -New attributes added: - -- `db.system.name` — e.g. `postgresql`, `sqlite`, `cosmosdb`, `memory`, `disk` -- `db.operation.name` — e.g. `find`, `all`, `filter`, `set` -- `db.query.text` — sanitized / parameterized SQL or Cosmos query (no bound values) -- `messaging.operation.name` — `publish`, `process`, `receive` - -Breaking: dashboards/alerts keying on the previous attribute names must be -updated. Queue consumer spans no longer log raw message bodies — use -`messaging.message.id` and `messaging.message.type` instead. diff --git a/.changeset/payload-summary-recurse.md b/.changeset/payload-summary-recurse.md deleted file mode 100644 index 05b52da87..000000000 --- a/.changeset/payload-summary-recurse.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Capture more of the rpc request payload in the `rpc.request.payload` span -attribute. - -The summarizer was one level deep: top-level scalars were kept, but any nested -object collapsed to `Object[N]` and any array to `Array[N]`, hiding useful -inputs (carrier, packageType, dimensions, ids, per-item amounts, …). - -It now recurses into nested objects up to `PAYLOAD_MAX_DEPTH` (4) and samples -arrays to their first `PAYLOAD_ARRAY_HEAD` (20) elements, appending a `…N more` -marker when longer — so item arrays stay diagnosable without dumping the whole -tail on high-frequency commands. Long strings are still snipped at 256 chars, -and `password`/`secret`/`token` keys are redacted at any depth (previously only -a top-level `password`). diff --git a/.changeset/per-namespace-seeding.md b/.changeset/per-namespace-seeding.md deleted file mode 100644 index e2a8e3570..000000000 --- a/.changeset/per-namespace-seeding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add per-namespace seeding to SQL (SQLite/PostgreSQL) and CosmosDB store adapters. Previously only the `primary` namespace was seeded at initialization; now each namespace is lazily seeded on first access using `Effect.cached` to guarantee at-most-once execution. Primary namespace continues to seed eagerly on initialization. CosmosDB uses namespace-specific marker documents for backward compatibility. diff --git a/.changeset/petite-tables-cover.md b/.changeset/petite-tables-cover.md deleted file mode 100644 index 0dbc58797..000000000 --- a/.changeset/petite-tables-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix withDefault diff --git a/.changeset/polite-chefs-drum.md b/.changeset/polite-chefs-drum.md deleted file mode 100644 index 983b6be8b..000000000 --- a/.changeset/polite-chefs-drum.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -fix typo diff --git a/.changeset/pre.json b/.changeset/pre.json deleted file mode 100644 index f58007273..000000000 --- a/.changeset/pre.json +++ /dev/null @@ -1,496 +0,0 @@ -{ - "mode": "pre", - "tag": "beta", - "initialVersions": { - "@effect-app/cli": "2.0.0", - "effect-app": "3.16.0", - "@effect-app/eslint-codegen-model": "1.47.0", - "@effect-app/eslint-shared-config": "0.5.6", - "@effect-app/infra": "3.10.0", - "@effect-app/vue": "2.94.0", - "@effect-app/vue-components": "3.2.0", - "@effect-app/e2e": "4.0.0-beta.282" - }, - "changesets": [ - "active-query-invalidation", - "add-oxlint-integration", - "add-stream-mutation", - "adopt-module-system", - "align-invalidation-entry-types", - "align-schema-void-ts-semantics", - "all-phones-dream", - "amber-garlics-sing", - "api-client-isolate-layers", - "atom-query-vue", - "batch-cosmos-acks", - "batch-dual-options", - "big-teams-wash", - "blue-rats-admire", - "bold-berries-change", - "brave-animals-like", - "brave-bushes-fold", - "brave-laws-add", - "brave-oranges-try", - "brave-years-fry", - "bright-camels-grin", - "bright-lions-share", - "bright-mugs-paint", - "bright-peaches-wave", - "bright-trees-count", - "builtin-map-union-receiver", - "cached-per-request-helper", - "calm-clocks-observe", - "calm-cosmos-cluster", - "calm-filter-cycle", - "calm-pallets-listen", - "calm-pets-float", - "calm-picks-refresh", - "chilly-colts-attack", - "chilly-steaks-rule", - "chubby-dolls-yell", - "class-facade-encode-keys", - "clean-atom-suspense", - "clean-sql-data-column", - "clean-unused-dependencies", - "cleanup-tsgolint-codegen", - "clear-cycles-enjoy", - "clever-breads-enter", - "clever-pets-follow", - "cli-preserve-trailing-newline", - "cli-sync-no-tags", - "cli-ue-semver-target", - "client-fetch-property", - "cluster-servicebus-notifications", - "codegen-rule-options", - "cold-brooms-ski", - "cold-rabbits-pay", - "command-button-optional-input", - "commander-state-toast-types", - "commander-stream-fn", - "common-schools-stop", - "computed-projections-extensions", - "computed-projections-infra", - "concurrent-decode-helpers", - "config-nested-native", - "configurable-barrel-import-extension", - "configurable-mutation-invalidation", - "context-map-resolver-cache", - "cool-needles-wash", - "core-boundary-split", - "cosmos-autoscale-containers", - "cosmos-cluster-occ-tests", - "cosmos-every-param-shift", - "cosmos-filter-seed", - "cosmos-namespace-spans", - "cosmos-namespace-support", - "cosmos-span-response-size", - "cosmos-workflow-engine", - "cozy-cats-lie", - "crisp-seals-care", - "crisp-trees-smash", - "ctxmap-per-request-memomap", - "cuddly-laws-smell", - "cups-error-tagged", - "curly-beds-sing", - "curly-dolls-double", - "curvy-files-share", - "curvy-pugs-rush", - "cyan-experts-smoke", - "cyan-pianos-peel", - "cyan-things-train", - "date-defaults-pipe-self", - "date-identifier-split", - "delay-waiting-toast", - "disable-query-invalidation", - "document-query-project-modes", - "drop-constructor-default", - "easy-aliens-peel", - "easy-spiders-study", - "effa-sync-shared", - "effa-sync-update-flag", - "effect-app-arbitrary-email-phone", - "effect-app-prefer-fn-untraced", - "effect-app-realistic-number-samples", - "effect-main-rc-apis", - "eighty-aliens-hide", - "eighty-signs-smile", - "eleven-eyes-lie", - "empty-queens-begin", - "empty-rules-check", - "eslint-codegen-glob-11", - "expose-make-on-query-command", - "extract-fixed-nuxt-error-boundary", - "facade-hardening-error-facade", - "fair-baboons-marry", - "fair-oranges-wave", - "fair-tables-march", - "famous-plums-watch", - "fancy-ducks-run", - "fast-loops-scream", - "fiery-feet-sleep", - "fiery-suits-kneel", - "five-trams-repeat", - "fix-class-type-export", - "fix-cli-gist-cache-id", - "fix-client-input-stream", - "fix-contextmap-leak", - "fix-cosmos-cluster-deferred", - "fix-cosmos-cluster-machineid", - "fix-cosmos-serverless-create", - "fix-cosmos-workflow-engine", - "fix-is-generator-function", - "fix-json-schema-annotations", - "fix-jsonschema-postprocess", - "fix-onedsl-r-inference", - "fix-pascalcase-barrel", - "fix-path-recursive-index-signature", - "fix-project-encodekeys-selection", - "fix-projection-union-adapter", - "fix-query-defaultvalues-double-encode", - "fix-repo-transformed-id-union", - "fix-repo-transformed-id", - "fix-resolver-cache-precheck", - "fix-schema-interface-widening", - "fix-seed-marker-and-circular-dep", - "fix-sql-array-relation-queries", - "fix-sql-in-null-handling", - "fix-sql-includes-serialize", - "fix-sql-json-from-string-codec", - "fix-sql-namespace-params", - "fix-sql-pg-seed-migrations-table", - "fix-sql-select-etag-column", - "fix-sql-select-types-and-defaults", - "fix-sql-whereevery-flip", - "fix-sqlite-per-ns-seed-migrations-table", - "fix-transformed-struct-defaults", - "fix-vue-form-numeric-type-detection", - "fix-vue-interrupt-tests", - "fix-vue-lint-typescript-version", - "flat-turkeys-invent", - "flatten-nested-anyof", - "forty-bottles-tease", - "frank-ends-switch", - "frank-memes-sin", - "frank-papayas-design", - "fresh-buckets-pump", - "fresh-candles-shake", - "fresh-indexed-fields", - "fresh-mails-visit", - "fresh-paths-say", - "fresh-pianos-argue", - "friendly-points-drum", - "fruity-meteors-fix", - "full-oranges-heal", - "full-pugs-fall", - "funky-guests-switch", - "funny-webs-clap", - "fuzzy-eels-decide", - "fuzzy-eggs-jump", - "gentle-bats-leave", - "gentle-donkeys-tease", - "gold-ghosts-laugh", - "gold-wolves-shake", - "green-coins-talk", - "green-frogs-check", - "green-snakes-poke", - "green-taxis-project-hash", - "green-taxis-rule", - "handle-mutate-request-always-functions", - "handler-input-make-aligned", - "heavy-bears-appear", - "heavy-ducks-show", - "honest-planets-fry", - "http-span-name", - "huge-carrots-deny", - "humble-walls-fail", - "icy-paws-return", - "indefinite-waiting-toast", - "infra-generate-from-schema-seed", - "infra-prefer-fn-untraced", - "infra-sentry-node-dep", - "invalidation-set-add-shortcut-and-batch", - "itchy-mangos-find", - "jitm-json-store-boundary", - "khaki-frogs-wave", - "kind-lizards-bathe", - "large-pugs-strive", - "late-forks-obey", - "lazy-boats-lay", - "lazy-model-facades", - "legal-weeks-raise", - "lemon-emus-behave", - "little-berries-sip", - "little-planets-bake", - "live-query-dependencies", - "long-regions-change", - "loud-planets-cheer", - "lucky-papayas-fetch", - "lucky-streets-add", - "lucky-zebras-invent", - "major-drinks-stop", - "major-planets-lie", - "many-ideas-report", - "meta-codegen-no-export", - "meta-module-name-override", - "metal-mammals-smell", - "middleware-error-from-dynamic-rcm", - "middleware-errors-via-rpc-tag", - "mighty-hoops-help", - "migrate-tsgolint", - "modern-humans-chew", - "moody-peas-smoke", - "mutate-invalidation-shorthands", - "mutate-wrap-stream-no-args", - "namespaced-change-feed", - "native-encoded-query-json", - "neat-brooms-wave", - "neat-dingos-tease", - "neat-pumpkins-juggle", - "new-badgers-wait", - "new-berries-march", - "nice-jars-grow", - "nice-needles-film", - "nice-shrimps-fold", - "ninety-stamps-end", - "no-await-effect", - "number-input-dual-separator", - "odd-deers-work", - "odd-tigers-scope", - "old-snakes-film", - "old-suns-kneel", - "olive-onions-lose", - "omega-native-input-types", - "omega-nullable-nested-struct", - "omegaform-between-localization", - "omegaform-decodeto-meta-from-encoded", - "omegaform-enter-submit-repro", - "omegaform-input-registry", - "omegaform-module-split", - "omegaform-typecheck-stories", - "omegaform-vnumber-input", - "optimize-codegen-barrel-model", - "otel-semconv-spans", - "payload-summary-recurse", - "per-namespace-seeding", - "petite-tables-cover", - "polite-chefs-drum", - "project-computed-type-safety", - "project-encoded-type-constraint", - "project-union-schema", - "project-union-subsets", - "projectable-from-domain", - "projectable-key-guard-project", - "provide-dual", - "publish-tsconfig-postpack", - "pure-usesync-cleanup", - "query-aggregate-support", - "query-map-has-ops", - "query-optional-mode", - "query-span-propagation", - "queryraw-stored-json", - "quick-seals-relate", - "quick-wolves-yawn", - "quiet-shrimps-juggle", - "quiet-tigers-reply", - "quiet-tools-rest", - "rare-llamas-begin", - "rare-rules-help", - "ready-bags-wave", - "ready-bananas-scream", - "real-lilies-design", - "real-suits-clean", - "recover-stuck-query-interrupts", - "red-pillows-repeat", - "relation-collect-fields", - "remove-by-id-array", - "remove-class-struct-pick-omit", - "remove-legacy-from-make-client", - "remove-legacy-mutate-result-2", - "remove-lodash-from-effect-app", - "remove-operations", - "remove-queue-machinery", - "remove-tagged-error-compat", - "remove-tagged-request", - "remove-void-schema-override", - "remove-withDefaultConstructor", - "rename-mutate-stream", - "rename-withdefault-to-withconstructordefault", - "replace-proxify-with-accessors", - "repo-derived-query-invalidation", - "repo-mutations-accept-empty-arrays", - "repo-span-topology", - "repo-spans-itemtype", - "repository-registry", - "request-scoped-dependencies", - "require-middleware-layers", - "resolver-cache-use-official", - "restore-tight-project-guard", - "router-no-meta", - "rpc-stream-support", - "salty-weeks-walk", - "schema-concurrency-unbounded", - "schema-encode-slow-metrics", - "schema-facade-identifier-structfacade", - "schema-json-collections", - "secret-url-tests", - "select-delayed-cache-invalidation", - "sendgrid-configurable-fake-address", - "server-driven-cache-invalidation", - "server-invalidation-handler-shorthand", - "servicebus-log-only-spans", - "shaggy-terms-show", - "shaggy-waves-slide", - "shaggy-wolves-grow", - "share-oxlintrc-config", - "sharp-buses-bow", - "sharp-seals-clean", - "short-cats-march", - "short-frogs-add", - "short-suns-travel", - "shy-squids-make", - "silent-results-shave", - "silent-rooms-throw", - "silly-eggs-roll", - "single-invalidate-query-call", - "six-regions-start", - "six-yaks-stop", - "sixty-meals-sin", - "slimy-apes-film", - "slimy-lions-laugh", - "slimy-schools-fold", - "slow-readers-wave", - "small-papers-sneeze", - "small-wolves-knock", - "smart-beers-listen", - "smart-candles-rest", - "soft-books-win", - "soft-guests-relax", - "soft-mails-brush", - "soft-work-invalidations", - "sour-turtles-smash", - "source-first-package-exports", - "sparkly-cars-press", - "spicy-falcons-rush", - "spicy-lights-trade", - "sql-namespace-support", - "sql-store-adapter", - "sql-transaction-middleware", - "sqlite-per-namespace-layermap", - "sqlite-workflow-engine", - "sse-context-map-scope", - "sse-filter-namespace-by-storeid", - "staged-publish-directory", - "stale-actors-remain", - "stale-emus-see", - "stale-papers-join", - "store-decode-schema-error", - "store-effect-cached", - "store-json-values", - "store-seed-uninterruptible", - "stream-callable-progress", - "stream-drain-data-dependencies", - "stream-effect-fail", - "stream-final-default-success", - "stream-final-type", - "stream-first-write-flush", - "stream-fn-enhancements", - "stream-metadata-v1", - "stream-on-command-query", - "stream-withdefaulttoast", - "strict-stream-handlers", - "stringid-native-arbitrary", - "strong-dots-sin", - "struct-pick-omit", - "structfacade-effect-app-struct", - "suspense-keep-previous-value", - "sweet-humans-ring", - "sweet-moments-see", - "swift-owls-knit", - "swift-tables-turn", - "sync-effect-app-command", - "sync-effect-subtree", - "sync-effect-url", - "sync-push-deletions", - "synchronous-change-feed", - "tagged-request-command-query-split", - "tagged-request-for", - "tagged-union-drop-fields-constraint", - "tagged-union-generate-guards", - "tagged-union-generate-guards2", - "tall-boxes-relax", - "tall-hats-reflect", - "tanstack-effect-equal-structural-sharing", - "tanstack-suspense-seed", - "tasty-eggs-stare", - "tasty-fans-accept", - "ten-crews-like", - "ten-suns-shine", - "tender-bars-ring", - "tender-donkeys-pick", - "thin-zebras-press", - "thirty-cooks-pump", - "thirty-experts-taste", - "three-hotels-roll", - "tidy-caches-reflect", - "tidy-cosmos-batches", - "tidy-derived-dependencies", - "tidy-dprint-oxlint-config", - "tidy-effects-diagnose", - "tidy-forms-report", - "tidy-pans-rule", - "tidy-poets-think", - "tidy-remaining-formatting", - "tidy-rivers-identify", - "tidy-titles-remove", - "tiny-cows-repair", - "tiny-gorillas-jump", - "tired-insects-stand", - "toast-group-request-id", - "tough-apes-whisper", - "tsgo-decodingservices-workaround", - "tsgo-direct-schema-property-access", - "twenty-seas-grab", - "two-clocks-wash", - "typed-database-error", - "typed-query-builders", - "unify-emailer-spans", - "unify-parsemany-spans", - "update-effect-4.0.0", - "update-effect-beta-107", - "update-effect-beta-36", - "update-effect-beta-52", - "update-effect-beta-60-rpc-custom", - "update-effect-beta-66", - "update-effect-beta-67", - "update-effect-beta-83", - "update-effect-beta-84", - "update-effect-beta-86", - "update-effect-beta-88", - "update-effect-beta-90", - "update-effect-pkgprnew", - "update-effect-rc-112", - "update-effect-rc-115", - "upset-aliens-bathe", - "upset-bugs-roll", - "version-independent-snapshot", - "vue-components-src-exports", - "vue-components-vuetify-typecheck", - "vue-placeholder-data-fallback", - "vue-prefer-fn-untraced", - "vue-stream-query", - "vue-suspense-result-race", - "vue-unified-invalidation-foreach", - "wacky-deer-decide", - "warm-maps-reflect", - "warm-streets-smell", - "wet-roses-press", - "wet-sites-fall", - "wild-olives-bathe", - "wild-wombats-nail", - "wise-items-warn", - "wise-stamps-crash", - "withdefaulttoaststream-progress", - "witty-bats-return", - "wrap-stream-command" - ] -} diff --git a/.changeset/project-computed-type-safety.md b/.changeset/project-computed-type-safety.md deleted file mode 100644 index 0b4384175..000000000 --- a/.changeset/project-computed-type-safety.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Tighten projectComputed projection schema typing against encoded repo fields and computed output types. diff --git a/.changeset/project-encoded-type-constraint.md b/.changeset/project-encoded-type-constraint.md deleted file mode 100644 index 3224826e2..000000000 --- a/.changeset/project-encoded-type-constraint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Constrain `project` schema to require matching `Encoded` type with original success schema diff --git a/.changeset/project-union-schema.md b/.changeset/project-union-schema.md deleted file mode 100644 index ab4b331d7..000000000 --- a/.changeset/project-union-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Allow query projections to use a repository's full union schema. diff --git a/.changeset/project-union-subsets.md b/.changeset/project-union-subsets.md deleted file mode 100644 index cfcd3e5bd..000000000 --- a/.changeset/project-union-subsets.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Fix nested DTO subset projection inference for tagged unions. diff --git a/.changeset/projectable-from-domain.md b/.changeset/projectable-from-domain.md deleted file mode 100644 index c60797afa..000000000 --- a/.changeset/projectable-from-domain.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Tag-aware `ProjectableFromDomain` for `projectComputed`: projection Encoded fields must exist on the matching domain tagged state (or be computed). Prevents Overview.List SchemaErrors when cancel states omit workflow lock fields like `activeRequest`. diff --git a/.changeset/projectable-key-guard-project.md b/.changeset/projectable-key-guard-project.md deleted file mode 100644 index 45052bae9..000000000 --- a/.changeset/projectable-key-guard-project.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Keep tag-aware `ProjectableFromDomain` for `projectComputed` only; restore loose key-presence guard for `project()` so view DTOs with reshaped fields keep typechecking. diff --git a/.changeset/provide-dual.md b/.changeset/provide-dual.md deleted file mode 100644 index 2faa2bc68..000000000 --- a/.changeset/provide-dual.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Make `Schema.provide` dual (pipeable) diff --git a/.changeset/publish-tsconfig-postpack.md b/.changeset/publish-tsconfig-postpack.md deleted file mode 100644 index 18c17292d..000000000 --- a/.changeset/publish-tsconfig-postpack.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/cli": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"effect-app": patch ---- - -Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. diff --git a/.changeset/pure-usesync-cleanup.md b/.changeset/pure-usesync-cleanup.md deleted file mode 100644 index 31dd061c1..000000000 --- a/.changeset/pure-usesync-cleanup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Refactor `Pure` helpers to use typed `useSync` calls instead of unsafe casts. diff --git a/.changeset/query-aggregate-support.md b/.changeset/query-aggregate-support.md deleted file mode 100644 index a0a533f88..000000000 --- a/.changeset/query-aggregate-support.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add aggregate query support: `Q.aggregate(schema, aggregateMap)` performs GROUP BY + aggregate functions at the database level (Memory, SQL, Cosmos). - -- New `agg` DSL namespace: `agg.field(path)` (group-by), `agg.count()`, `agg.countWhen(op)`, `agg.sum(field)`, `agg.min(field)`, `agg.max(field)` -- New `aggregate(schema, aggregateMap)` query operator replaces in-memory grouping with a single DB-level query -- Memory store: pure JS group-by + aggregation -- SQL store: `GROUP BY` + `COUNT(CASE WHEN ...)` / `SUM` / `MIN` / `MAX` -- Cosmos store: `GROUP BY` + `SUM(IIF(...))` for conditional counts diff --git a/.changeset/query-map-has-ops.md b/.changeset/query-map-has-ops.md deleted file mode 100644 index 62a7d17bc..000000000 --- a/.changeset/query-map-has-ops.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -Query maps as JSON arrays of `[key, value]` tuples. - -`where("meta", "hasKey" | "hasValue" | "hasKeyValue", ...)` (and `not*` / `*-any` / `*-all` variants) filter `ReadonlyMap` fields. Memory, Disk, SQLite, Postgres, and Cosmos compile those ops against the encoded tuple array. diff --git a/.changeset/query-optional-mode.md b/.changeset/query-optional-mode.md deleted file mode 100644 index c28dbb23a..000000000 --- a/.changeset/query-optional-mode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add `mode: "optional"` overload for queries. When `mode: "optional"` is set, the `arg` must be a `WatchSource>`. The query is disabled (`enabled: false`) when the option is `None`, and enabled with the unwrapped value when `Some`. diff --git a/.changeset/query-span-propagation.md b/.changeset/query-span-propagation.md deleted file mode 100644 index 16dc577f7..000000000 --- a/.changeset/query-span-propagation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Preserve Effect parent spans across query refetch and invalidation. diff --git a/.changeset/queryraw-stored-json.md b/.changeset/queryraw-stored-json.md deleted file mode 100644 index 7f1f47705..000000000 --- a/.changeset/queryraw-stored-json.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Memory, SQLite, and Postgres `Store.queryRaw` now run over stored JSON documents, not Encoded rows. `Repository.queryRaw` already applies `toCodecJson` to the projector output, so native Encoded `Date` / `Map` / `Set` values no longer fail `Expected JSON value`. Cosmos was already JSON and is unchanged. - -Memory projectors that assumed native Encoded `Date` / `Map` / `Set` values now see the stored JSON form (ISO strings, arrays). diff --git a/.changeset/quick-seals-relate.md b/.changeset/quick-seals-relate.md deleted file mode 100644 index 3312d8ef0..000000000 --- a/.changeset/quick-seals-relate.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Update Effect dependencies to 4.0.0-beta.28 across workspace packages. diff --git a/.changeset/quick-wolves-yawn.md b/.changeset/quick-wolves-yawn.md deleted file mode 100644 index a9c5fb5fa..000000000 --- a/.changeset/quick-wolves-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix: isA diff --git a/.changeset/quiet-shrimps-juggle.md b/.changeset/quiet-shrimps-juggle.md deleted file mode 100644 index 15066e680..000000000 --- a/.changeset/quiet-shrimps-juggle.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch ---- - -Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. diff --git a/.changeset/quiet-tigers-reply.md b/.changeset/quiet-tigers-reply.md deleted file mode 100644 index 406e3f2ef..000000000 --- a/.changeset/quiet-tigers-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -bs diff --git a/.changeset/quiet-tools-rest.md b/.changeset/quiet-tools-rest.md deleted file mode 100644 index 06de1397e..000000000 --- a/.changeset/quiet-tools-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- - ---- - -Run T3 worktree dependency preparation asynchronously while retaining portable Git hook setup. diff --git a/.changeset/rare-llamas-begin.md b/.changeset/rare-llamas-begin.md deleted file mode 100644 index 0e1478d01..000000000 --- a/.changeset/rare-llamas-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -allow excess props in openapi schemas diff --git a/.changeset/rare-rules-help.md b/.changeset/rare-rules-help.md deleted file mode 100644 index b19cdd872..000000000 --- a/.changeset/rare-rules-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fix OmegaForm required override handling so omitted boolean props fall back to schema metadata. diff --git a/.changeset/ready-bags-wave.md b/.changeset/ready-bags-wave.md deleted file mode 100644 index 2ecf17d84..000000000 --- a/.changeset/ready-bags-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -unify runPromise diff --git a/.changeset/ready-bananas-scream.md b/.changeset/ready-bananas-scream.md deleted file mode 100644 index 8db9c8e07..000000000 --- a/.changeset/ready-bananas-scream.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update packages diff --git a/.changeset/real-lilies-design.md b/.changeset/real-lilies-design.md deleted file mode 100644 index 678429a71..000000000 --- a/.changeset/real-lilies-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -update to latest effect diff --git a/.changeset/real-suits-clean.md b/.changeset/real-suits-clean.md deleted file mode 100644 index 75d7ca00c..000000000 --- a/.changeset/real-suits-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix diff --git a/.changeset/recover-stuck-query-interrupts.md b/.changeset/recover-stuck-query-interrupts.md deleted file mode 100644 index af5ba699f..000000000 --- a/.changeset/recover-stuck-query-interrupts.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Recover interrupted queries and classify query-side suspense interrupts. - -- A query fetch that is interrupted (a subscriber lost interest, a refresh superseded it, or the component navigated) can be left `waiting = true` with no fiber running — effect-core hides the interrupt by removing the result-observer before interrupting, so it is never written back. `isStaleResult`/swr short-circuit on `waiting`, so that state is never revalidated and becomes **terminal** (a cold suspense read throws → blank page; a warm one serves stale forever). `recoverStuckWaitingOnMount` now treats a `waiting` result with `inFlight === 0` as not-yet-fetched and refetches it on mount. Interrupts are **not** auto-retried — recovery only fires for a genuine (re)mounting observer, and a live in-flight fetch (`inFlight > 0`) is joined, not superseded. Concurrent mounts share a single recovery fetch. Exposes `queryFetchStates` / `QueryFetchState`. - -- `useSuspenseQuery` / `useSuspenseQueryNew` now convert an interrupt-only failure that settles **while the component is still mounted** into a typed `SuspenseInterruptedError` (a `Data.TaggedError` carrying the original cause). Such an interrupt cannot be a navigation/unmount cancel — that path interrupts the suspense fiber itself and never yields a still-mounted `Failure` exit — so it is query-side (e.g. a server RPC interrupt `Exit` delivered over a 200). Error boundaries can render it ("the request was interrupted — reload?") instead of silencing it into a blank page, while continuing to silence genuine navigation interrupts. Adds `SuspenseInterruptedError` / `isSuspenseInterruptedError`. diff --git a/.changeset/red-pillows-repeat.md b/.changeset/red-pillows-repeat.md deleted file mode 100644 index 39d5ef5fd..000000000 --- a/.changeset/red-pillows-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -update packages diff --git a/.changeset/relation-collect-fields.md b/.changeset/relation-collect-fields.md deleted file mode 100644 index b3d55d8d3..000000000 --- a/.changeset/relation-collect-fields.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add `collectFields`/`collectDistinctFields` to `relation()` DSL for collecting values from multiple fields of relation items into a single flattened array. - -Example: given `T = { a: boolean, items: { x: string, y: string, z: number }[] }[]`, you can now collect all distinct `x` and `y` values into a single `string[]`: - -```ts -relation("items").collectDistinctFields(["x", "y"]) -``` diff --git a/.changeset/remove-by-id-array.md b/.changeset/remove-by-id-array.md deleted file mode 100644 index 7d926594e..000000000 --- a/.changeset/remove-by-id-array.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -`removeById` now takes `id | NonEmptyReadonlyArray` instead of a variadic rest, and the extended repo's `removeById` accepts a `batch` option to chunk large deletes — consistent with `save` and `remove`. diff --git a/.changeset/remove-class-struct-pick-omit.md b/.changeset/remove-class-struct-pick-omit.md deleted file mode 100644 index 5a69ca104..000000000 --- a/.changeset/remove-class-struct-pick-omit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Remove pick/omit customizations from Class/TaggedClass/Struct/TaggedStruct. Use `Struct.pick(X.fields, [...])` from `effect-app` instead. diff --git a/.changeset/remove-legacy-from-make-client.md b/.changeset/remove-legacy-from-make-client.md deleted file mode 100644 index 83b16c604..000000000 --- a/.changeset/remove-legacy-from-make-client.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Remove `legacy` from `makeClient` return and clean up `LegacyMutation`, `LegacyMutationImpl`, and related types. diff --git a/.changeset/remove-legacy-mutate-result-2.md b/.changeset/remove-legacy-mutate-result-2.md deleted file mode 100644 index 987fd0d4e..000000000 --- a/.changeset/remove-legacy-mutate-result-2.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Remove legacy `mutateToResult` stream mutation factory, `wrapStream` command builder, and standard command progress reporting (`running`/`progress` from the old stream factory path). Keep only the `mutate` path for use with `streamFn` combinators. Remove obsolete types (`StreamMutationWithExtensions`, `StreamCommandWithExtensions`, `StreamFnExtension`, `MutateStreamCallOptions`) and the internal `makeStreamMutation` function. Rename `mutate.wrapStream` shorthand to `mutate.wrap` to match the non-stream convention. diff --git a/.changeset/remove-lodash-from-effect-app.md b/.changeset/remove-lodash-from-effect-app.md deleted file mode 100644 index b0e38be2e..000000000 --- a/.changeset/remove-lodash-from-effect-app.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Remove lodash type imports from utils.ts, replacing with native TypeScript equivalents. diff --git a/.changeset/remove-operations.md b/.changeset/remove-operations.md deleted file mode 100644 index 8911cc433..000000000 --- a/.changeset/remove-operations.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/infra": patch -"effect-app": patch -"@effect-app/vue": patch ---- - -Remove Operations service and repo diff --git a/.changeset/remove-queue-machinery.md b/.changeset/remove-queue-machinery.md deleted file mode 100644 index 280d159d2..000000000 --- a/.changeset/remove-queue-machinery.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/infra": patch -"effect-app": patch ---- - -Remove obsolete queue machinery left unused after the move to cluster entities: the SB/SQLite/mem `QueueMaker` implementations (`QueueMaker/{SQLQueue,memQueue,sbqueue}.ts`), their `ServiceBus.ts`/`memQueue.ts` transports, and `RequestFiberSet.ts` (its `setRootParentSpan` helper is inlined into `MainFiberSet`). `effect-app/QueueMaker` keeps only `QueueMeta`; the `QueueBase` interface and empty `QueueMaker` ops object are dropped. diff --git a/.changeset/remove-tagged-error-compat.md b/.changeset/remove-tagged-error-compat.md deleted file mode 100644 index 66c2f2a4c..000000000 --- a/.changeset/remove-tagged-error-compat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Remove `TaggedError` compatibility re-export, use `TaggedErrorClass` directly diff --git a/.changeset/remove-tagged-request.md b/.changeset/remove-tagged-request.md deleted file mode 100644 index 80885a1c4..000000000 --- a/.changeset/remove-tagged-request.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Remove `TaggedRequest` from `makeRpcClient`, now only `TaggedRequestFor` is returned. Remove all legacy `meta.moduleName` support — `id` and `moduleName` are now required on `Req` type. Remove `makeRpcGroup` (use `makeRpcGroupFromRequestsAndModuleName` instead). diff --git a/.changeset/remove-void-schema-override.md b/.changeset/remove-void-schema-override.md deleted file mode 100644 index ec58a9596..000000000 --- a/.changeset/remove-void-schema-override.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Remove the local `Schema.Void` / `SchemaAST.Void` override. effect `4.0.0-beta.90` ships the TypeScript `void` parser semantics upstream (`SchemaAST.Void.getParser()` → `fromAnyToConst(undefined)`, the effect-smol PR #2475 behavior), so `S.Void` already accepts any present value and discards it to `undefined`. `Schema.Void` now re-uses effect's `S.Void` and `SchemaAST` is a plain re-export of `effect/SchemaAST`. The unused `Void_` alias is dropped. No behavior change. diff --git a/.changeset/remove-withDefaultConstructor.md b/.changeset/remove-withDefaultConstructor.md deleted file mode 100644 index 26c595ff7..000000000 --- a/.changeset/remove-withDefaultConstructor.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": patch -"@effect-app/vue-components": patch ---- - -Remove `withDefaultConstructor` wrapper, use `S.withConstructorDefault` directly with `Effect.succeed`/`Effect.sync`. diff --git a/.changeset/rename-mutate-stream.md b/.changeset/rename-mutate-stream.md deleted file mode 100644 index c75f8de1b..000000000 --- a/.changeset/rename-mutate-stream.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Rename stream mutation helpers: `mutateStream` → `mutateToResult`, `mutateStream2` → `mutate`. diff --git a/.changeset/rename-withdefault-to-withconstructordefault.md b/.changeset/rename-withdefault-to-withconstructordefault.md deleted file mode 100644 index 936806a6c..000000000 --- a/.changeset/rename-withdefault-to-withconstructordefault.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch ---- - -Rename `withDefault` schema extensions to `withConstructorDefault` for clarity. - -Document that `.withConstructorDefault` is **construction-only** (applied during `.make(...)` when a field is omitted) and is **not** applied during decode — it cannot be used to JIT-migrate database fields. Per-property JSDoc on every `.withConstructorDefault` / `.withDecodingDefaultType` exposed by `Schema/ext.ts`, `Schema/numbers.ts`, `Schema/moreStrings.ts`, and `ids.ts` so the caveat is visible on hover. - -Re-export `withConstructorDefault`, `withDecodingDefault`, `withDecodingDefaultKey`, `withDecodingDefaultType`, and `withDecodingDefaultTypeKey` from `effect-app/Schema` with explicit JSDoc. `withDecodingDefault*` is discouraged for persisted data: a missing field may be data corruption rather than an old-shape document, and silently substituting a default hides the problem. Prefer an explicit, preferably versioned migration of database data over decode-time fallbacks. diff --git a/.changeset/replace-proxify-with-accessors.md b/.changeset/replace-proxify-with-accessors.md deleted file mode 100644 index 931c8e6b4..000000000 --- a/.changeset/replace-proxify-with-accessors.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/vue": minor ---- - -Replace `proxify` with explicit service accessor helpers: `accessFn`, `accessEffectFn`, `accessCn`, `accessEffectCn`. diff --git a/.changeset/repo-derived-query-invalidation.md b/.changeset/repo-derived-query-invalidation.md deleted file mode 100644 index 6b6a6180b..000000000 --- a/.changeset/repo-derived-query-invalidation.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor -"@effect-app/vue": minor ---- - -Derive query invalidation from repository read/write dependencies and propagate dependency metadata through RPC clients. diff --git a/.changeset/repo-mutations-accept-empty-arrays.md b/.changeset/repo-mutations-accept-empty-arrays.md deleted file mode 100644 index 8d2f5de50..000000000 --- a/.changeset/repo-mutations-accept-empty-arrays.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Repository `save`, `remove`, and `removeById` now accept plain `ReadonlyArray` instead of `NonEmptyReadonlyArray`. Callers no longer need to narrow or guard for non-emptiness before invoking — empty inputs short-circuit to a no-op, making it ergonomic to pass through query results directly. diff --git a/.changeset/repo-span-topology.md b/.changeset/repo-span-topology.md deleted file mode 100644 index 3158abcba..000000000 --- a/.changeset/repo-span-topology.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Repository owns OTel span topology; store adapters annotate db.* semconv attrs on the current span instead of opening their own child spans. Eliminates 1-1 nested span chains (e.g. `Repository.query` → `Cosmos.filter`). Adds `annotateDb` helper alongside `withDbSpan` in `otel.ts`. Repo public ops (`find`, `all`, `query`, `queryRaw`, `mapped.*`) get explicit `Repository.` spans; internal codec steps (`encodeMany`, `parseMany`, `parseMany2`) keep bare names. `validateSample` opens per-iteration sub-spans to prevent attribute clobber. Adds WeakMap decoder cache for `parseMany2` schemas. diff --git a/.changeset/repo-spans-itemtype.md b/.changeset/repo-spans-itemtype.md deleted file mode 100644 index 57511e978..000000000 --- a/.changeset/repo-spans-itemtype.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add `itemType` annotation to all repository spans diff --git a/.changeset/repository-registry.md b/.changeset/repository-registry.md deleted file mode 100644 index 97dcc0339..000000000 --- a/.changeset/repository-registry.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add RepositoryRegistry service that tracks all repositories by modelName, with `seedNamespace` to seed all registered repos in parallel and `entries` to inspect the registry. - -Make non-primary namespace seeding explicit: Store and Repository now expose `seedNamespace` instead of auto-seeding on access. Primary namespace is still seeded eagerly on initialization. - -SQL stores: table creation (`CREATE TABLE IF NOT EXISTS`) is now part of `seedNamespace` instead of running eagerly at store construction, ensuring tables for non-primary namespaces are only created when explicitly seeded. diff --git a/.changeset/request-scoped-dependencies.md b/.changeset/request-scoped-dependencies.md deleted file mode 100644 index f06594b26..000000000 --- a/.changeset/request-scoped-dependencies.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Use set-backed data dependencies and provide request-scoped dependency services through a shared root-checked helper. diff --git a/.changeset/require-middleware-layers.md b/.changeset/require-middleware-layers.md deleted file mode 100644 index f83130751..000000000 --- a/.changeset/require-middleware-layers.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Require middleware to flow through `makeRpcClient` and the live layer through `makeRouter`. - -### `makeRpcClient(middleware, generalErrors?)` - -Signature drops the `rcs` (request-context map wrapper) parameter. `rcs` was only load-bearing on the type side for `RequestConfig` inference; that information is now derived from `middleware.requestContextMap`. `middleware` is required — the previous "rcs + optional middleware" overload is gone. - -**Migration**: - -```diff --makeRpcClient(RequestContextMap, undefined, AppMiddleware) -+makeRpcClient(AppMiddleware) -``` - -For tests/clients without a real middleware, build a minimal stub (`{ requestContextMap, requestContext }`) or pass any value satisfying `ClientMiddleware`. - -### `makeRouter(middlewareLive)` - -`makeRouter()` no longer infers the live middleware layer from `meta.middleware.Default`. The Live layer is now passed explicitly to `makeRouter`, and the request classes only carry the middleware tag (schema-only). This decouples the router from any assumption that the middleware tag exposes a `Default` static. - -**Migration**: - -```diff --export const { Router, matchAll } = makeRouter() -+export const { Router, matchAll } = makeRouter(AppMiddleware.Default) -``` diff --git a/.changeset/resolver-cache-use-official.md b/.changeset/resolver-cache-use-official.md deleted file mode 100644 index 8796b7c82..000000000 --- a/.changeset/resolver-cache-use-official.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Rewrite `withRequestResolverCache` to use official `RequestResolver.withCache`, creating a cached resolver per ContextMap via `getOrCreateStoreEffect` with semaphore-guarded initialization. diff --git a/.changeset/restore-tight-project-guard.md b/.changeset/restore-tight-project-guard.md deleted file mode 100644 index 58d2bfc21..000000000 --- a/.changeset/restore-tight-project-guard.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Keep tag-aware `ProjectableGuard` on both `project()` and `projectComputed` (no loose key-only paper-over). - -Hardening: - -- single-literal tags allow dual same-tag domain variants (KeysOfUnion of matched members) -- multi-tag / string tags still require keys on every matched member -- optional projection/domain keys checked by key presence only (not optional-assignability) - -Call sites must project domain-owned fields per tagged state. diff --git a/.changeset/router-no-meta.md b/.changeset/router-no-meta.md deleted file mode 100644 index b270edc5e..000000000 --- a/.changeset/router-no-meta.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Router `matchFor` no longer requires `meta` property on resource when requests carry `moduleName` from `TaggedRequestFor` diff --git a/.changeset/rpc-stream-support.md b/.changeset/rpc-stream-support.md deleted file mode 100644 index dda463c45..000000000 --- a/.changeset/rpc-stream-support.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -Add Effect RPC `Stream` support to the wrapper. - -- New `Stream` request constructor on `TaggedRequestFor` parallel to `Query`/`Command`. Emits resources with `type: "stream"`. -- Server router (`@effect-app/infra` `routing.ts`) accepts stream resources whose handlers return a `Stream.Stream` (or a function from input to one). Forwards `stream: true` to `Rpc.make` so `RpcSchema.Stream` wrapping is applied. Streams bypass `applyRequestTypeInterruptibility` and the `Effect.withSpan` wrapping (the RPC server adds its own span). -- Client (`apiClientFactory.ts`) detects stream resources, forwards `stream: true` when constructing `RpcGroup`, and exposes the per-request `handler` as a `Stream.Stream` (via `Stream.unwrap` over the `ManagedRuntime` context) instead of an `Effect`. `Invalidation.CommandResponseWithMetaData` continues to apply only to commands. -- New `RequestStreamHandler` / `RequestStreamHandlerWithInput` shapes in `clientFor.ts`; `RequestHandlers` dispatches on `type: "stream"`. diff --git a/.changeset/salty-weeks-walk.md b/.changeset/salty-weeks-walk.md deleted file mode 100644 index c2fc23af0..000000000 --- a/.changeset/salty-weeks-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix request attr diff --git a/.changeset/schema-concurrency-unbounded.md b/.changeset/schema-concurrency-unbounded.md deleted file mode 100644 index 91341bcfd..000000000 --- a/.changeset/schema-concurrency-unbounded.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": minor ---- - -Add `concurrency: "unbounded"` parseOptions annotation to Schema constructors (Struct, Array, NonEmptyArray, Record, TaggedStruct, ReadonlySet, ReadonlyMap, Class, TaggedClass) so all encode/decode operations automatically run with unbounded concurrency. Also override `mapFields` on Struct and Class/TaggedClass to preserve the annotation. diff --git a/.changeset/schema-encode-slow-metrics.md b/.changeset/schema-encode-slow-metrics.md deleted file mode 100644 index da4f0eb03..000000000 --- a/.changeset/schema-encode-slow-metrics.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch ---- - -Improve repository schema encode/decode telemetry for event-loop tail analysis. - -- widen `app.schema.{encode,decode}.duration` histogram buckets into multi-second stalls -- count `app.schema.slow` (duration ≥ 100ms) with `app.entity` / operation attributes for alertable rates -- annotate spans with `app.schema.slow` and, on encode, `app.entity.state` from the first item's `_tag` diff --git a/.changeset/schema-facade-identifier-structfacade.md b/.changeset/schema-facade-identifier-structfacade.md deleted file mode 100644 index d2c07836c..000000000 --- a/.changeset/schema-facade-identifier-structfacade.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": minor ---- - -Schema/Class facades: add `StructFacade` and carry `identifier` on the class facades. - -- Add `StructFacade` = `Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> & { pinned type-level members }`. A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` model faceted by the `.d.ts`-emit compiler is retyped to it: still a real `S.Struct` (so `Workflow.AnyStructSchema`, the `Struct` reconstruction, `Union`, `.fields.x` keep working) while `Type`/`Encoded`/make/services resolve to the named namespace interfaces. -- Add `readonly identifier: string` to `OpaqueClassFacade` and `OpaqueErrorFacadeClass` (an `S.Class` static lost from the bare emitted interface, which the compiler otherwise had to hand-emit per model). `OpaqueFacade` is intentionally left untouched — `S.Opaque` and requests build on `S.Bottom`, not `S.Class`, so they have no `identifier`. diff --git a/.changeset/schema-json-collections.md b/.changeset/schema-json-collections.md deleted file mode 100644 index 0e74e3992..000000000 --- a/.changeset/schema-json-collections.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": minor ---- - -Add `ReadonlySetFromArray` and `ReadonlyMapFromArray` schema transformations that decode from annotated arrays to Set/Map. Update `ReadonlySet` and `ReadonlyMap` to use these internally. diff --git a/.changeset/secret-url-tests.md b/.changeset/secret-url-tests.md deleted file mode 100644 index 1a6c4f367..000000000 --- a/.changeset/secret-url-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Add SecretURL tests and switch secretURL config to use Config.nonEmptyString diff --git a/.changeset/select-delayed-cache-invalidation.md b/.changeset/select-delayed-cache-invalidation.md deleted file mode 100644 index 5a030621b..000000000 --- a/.changeset/select-delayed-cache-invalidation.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add `select` option to `MutationOptionsBase` for a second cache invalidation after long-running operations. - -When `select` is provided, cache invalidation fires twice: - -1. Immediately when the mutation completes (existing behaviour). -2. Again after the `select` effect finishes — useful for polling or waiting for a background job before refreshing data. - -```ts -useMutation(startExportCommand, { - select: (result) => pollUntilDone(result.jobId) -}) -``` diff --git a/.changeset/sendgrid-configurable-fake-address.md b/.changeset/sendgrid-configurable-fake-address.md deleted file mode 100644 index 5442509ff..000000000 --- a/.changeset/sendgrid-configurable-fake-address.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Add configurable `fakeMailAddress` to `SendgridConfig`. Supports `{i}` placeholder for unique addresses, e.g. `"test+{i}@example.com"`. diff --git a/.changeset/server-driven-cache-invalidation.md b/.changeset/server-driven-cache-invalidation.md deleted file mode 100644 index 6b3c38243..000000000 --- a/.changeset/server-driven-cache-invalidation.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@effect-app/infra": minor -"effect-app": minor -"@effect-app/vue": minor ---- - -Add server-driven cache invalidation via RPC response headers. - -- `effect-app/rpc`: new `Invalidation` module with `InvalidationKey` / `InvalidationKeys` schemas, `Invalidates` annotation (for declaring static invalidation on Rpc definitions), `InvalidationSet` reference (request-scoped accumulator), and `makeInvalidationSet` helper. -- `effect-app/middleware`: new `InvalidationMiddleware` RPC middleware tag; included in `DefaultGenericMiddlewares`. -- `effect-app/client`: new `InvalidationKeys` module with `InvalidationKeysFromServer` reference and `makeInvalidationKeysService` helper; `apiClientFactory` now taps HTTP responses to read the `x-invalidate` header and forward keys to `InvalidationKeysFromServer`. -- `@effect-app/infra`: new `InvalidationMiddlewareLive` RPC middleware implementation that owns the full lifecycle — creates a request-scoped `InvalidationSet` (backed by a `Ref`), pre-populates it from the `Invalidates` annotation, provides it to the handler, and after the handler completes registers an HTTP pre-response handler (via `appendPreResponseHandlerUnsafe`) to write the accumulated keys as an `x-invalidate` response header. No separate HTTP middleware is needed. -- `@effect-app/vue`: `invalidateQueries` / `useMutation` now reads server-provided invalidation keys from `InvalidationKeysFromServer` after each mutation and applies them alongside the client-side invalidation. diff --git a/.changeset/server-invalidation-handler-shorthand.md b/.changeset/server-invalidation-handler-shorthand.md deleted file mode 100644 index 298614e81..000000000 --- a/.changeset/server-invalidation-handler-shorthand.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect-app": patch ---- - -`InvalidationSet.add` accepts RPC handler shorthand. - -Server-side handlers may now pass an RPC handler object directly to -`InvalidationSet.add`; its query key is derived via `makeQueryKey`. Raw -`InvalidationKey` arrays continue to work. - -```ts -// before -yield* Invalidation.InvalidationSet.use(_ => _.add(makeQueryKey(UserRsc.GetMe))) - -// after -yield* Invalidation.InvalidationSet.use(_ => _.add(UserRsc.GetMe)) -``` diff --git a/.changeset/servicebus-log-only-spans.md b/.changeset/servicebus-log-only-spans.md deleted file mode 100644 index ea7d3207d..000000000 --- a/.changeset/servicebus-log-only-spans.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Drop OTel spans from ServiceBus client/sender/receiver/subscription lifecycle wrappers; keep `withLogSpan` + info logs only. Removes high-cardinality `sessionId` from log span names; promotes it to `messaging.session.id` log attribute via `Effect.annotateLogs`. diff --git a/.changeset/shaggy-terms-show.md b/.changeset/shaggy-terms-show.md deleted file mode 100644 index 97077f219..000000000 --- a/.changeset/shaggy-terms-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix bug diff --git a/.changeset/shaggy-waves-slide.md b/.changeset/shaggy-waves-slide.md deleted file mode 100644 index 6faf0cb89..000000000 --- a/.changeset/shaggy-waves-slide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix Req diff --git a/.changeset/shaggy-wolves-grow.md b/.changeset/shaggy-wolves-grow.md deleted file mode 100644 index d7dd9909d..000000000 --- a/.changeset/shaggy-wolves-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -align TaggedUnion with array arg diff --git a/.changeset/share-oxlintrc-config.md b/.changeset/share-oxlintrc-config.md deleted file mode 100644 index 525da9a23..000000000 --- a/.changeset/share-oxlintrc-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -Add shared oxlintrc.json base config. Consumers extend via `"extends": ["./node_modules/@effect-app/eslint-shared-config/src/oxlintrc.json"]`. Note: oxlint does not merge `ignorePatterns` — repeat the base patterns and add project-specific ones. diff --git a/.changeset/sharp-buses-bow.md b/.changeset/sharp-buses-bow.md deleted file mode 100644 index e87842529..000000000 --- a/.changeset/sharp-buses-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Refine schema copy behavior by keeping class copy constructor-based while using structural copy for Struct and TaggedStruct helpers. diff --git a/.changeset/sharp-seals-clean.md b/.changeset/sharp-seals-clean.md deleted file mode 100644 index bd6a57a07..000000000 --- a/.changeset/sharp-seals-clean.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -workaround middleware error issue diff --git a/.changeset/short-cats-march.md b/.changeset/short-cats-march.md deleted file mode 100644 index 4742ffd79..000000000 --- a/.changeset/short-cats-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -raw dog diff --git a/.changeset/short-frogs-add.md b/.changeset/short-frogs-add.md deleted file mode 100644 index 64dfc9706..000000000 --- a/.changeset/short-frogs-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix proxify diff --git a/.changeset/short-suns-travel.md b/.changeset/short-suns-travel.md deleted file mode 100644 index 3fc052656..000000000 --- a/.changeset/short-suns-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -Detect `Schema.Opaque()` model classes in the model preset while excluding `Context.Opaque` service tags. diff --git a/.changeset/shy-squids-make.md b/.changeset/shy-squids-make.md deleted file mode 100644 index dd94d003f..000000000 --- a/.changeset/shy-squids-make.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -bump diff --git a/.changeset/silent-results-shave.md b/.changeset/silent-results-shave.md deleted file mode 100644 index 94ce667e3..000000000 --- a/.changeset/silent-results-shave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -input mess diff --git a/.changeset/silent-rooms-throw.md b/.changeset/silent-rooms-throw.md deleted file mode 100644 index b9568f068..000000000 --- a/.changeset/silent-rooms-throw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fixes optionalKey in OmegaForm handling diff --git a/.changeset/silly-eggs-roll.md b/.changeset/silly-eggs-roll.md deleted file mode 100644 index 09533df76..000000000 --- a/.changeset/silly-eggs-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -use new schema features diff --git a/.changeset/single-invalidate-query-call.md b/.changeset/single-invalidate-query-call.md deleted file mode 100644 index 4444272b5..000000000 --- a/.changeset/single-invalidate-query-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Consolidate multiple `invalidateQueries` calls into a single call per group using a `predicate`, reducing the number of TanStack Query invalidation calls in the common case from N to 1. diff --git a/.changeset/six-regions-start.md b/.changeset/six-regions-start.md deleted file mode 100644 index 0855071e0..000000000 --- a/.changeset/six-regions-start.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -add missing Brand diff --git a/.changeset/six-yaks-stop.md b/.changeset/six-yaks-stop.md deleted file mode 100644 index ade76ec91..000000000 --- a/.changeset/six-yaks-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix boolean handling in SQL SELECT and WHERE diff --git a/.changeset/sixty-meals-sin.md b/.changeset/sixty-meals-sin.md deleted file mode 100644 index ffc4117f0..000000000 --- a/.changeset/sixty-meals-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix RequestName diff --git a/.changeset/slimy-apes-film.md b/.changeset/slimy-apes-film.md deleted file mode 100644 index a9a26d746..000000000 --- a/.changeset/slimy-apes-film.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -improve root union select diff --git a/.changeset/slimy-lions-laugh.md b/.changeset/slimy-lions-laugh.md deleted file mode 100644 index cd1b8574e..000000000 --- a/.changeset/slimy-lions-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -fix atom references diff --git a/.changeset/slimy-schools-fold.md b/.changeset/slimy-schools-fold.md deleted file mode 100644 index bbec62228..000000000 --- a/.changeset/slimy-schools-fold.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Update Effect dependencies to 4.0.0-beta.29 across workspace packages. diff --git a/.changeset/slow-readers-wave.md b/.changeset/slow-readers-wave.md deleted file mode 100644 index f2edbf31b..000000000 --- a/.changeset/slow-readers-wave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -switch to NdJson diff --git a/.changeset/small-papers-sneeze.md b/.changeset/small-papers-sneeze.md deleted file mode 100644 index 305b504a8..000000000 --- a/.changeset/small-papers-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -fix fn on stream semantics diff --git a/.changeset/small-wolves-knock.md b/.changeset/small-wolves-knock.md deleted file mode 100644 index bf1d95c1d..000000000 --- a/.changeset/small-wolves-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fup diff --git a/.changeset/smart-beers-listen.md b/.changeset/smart-beers-listen.md deleted file mode 100644 index 3ea43410a..000000000 --- a/.changeset/smart-beers-listen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch -"@effect-app/infra": patch ---- - -namespaces diff --git a/.changeset/smart-candles-rest.md b/.changeset/smart-candles-rest.md deleted file mode 100644 index 2e002783a..000000000 --- a/.changeset/smart-candles-rest.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/eslint-codegen-model": patch ---- - -Fold the encoded-override support from `ExtendedClass` and `ExtendedTaggedClass` into `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass`, and update model codegen to detect the new second-generic form. diff --git a/.changeset/soft-books-win.md b/.changeset/soft-books-win.md deleted file mode 100644 index b3b3b3637..000000000 --- a/.changeset/soft-books-win.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -update vue-components with more effect v4 diff --git a/.changeset/soft-guests-relax.md b/.changeset/soft-guests-relax.md deleted file mode 100644 index edaff71d2..000000000 --- a/.changeset/soft-guests-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -fix: vue components were bundling effect modules diff --git a/.changeset/soft-mails-brush.md b/.changeset/soft-mails-brush.md deleted file mode 100644 index 0b30a9b6d..000000000 --- a/.changeset/soft-mails-brush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Make OmegaInput's built-in input configuration props explicit so renderer attrs remain reserved for UI-library extensions. diff --git a/.changeset/soft-work-invalidations.md b/.changeset/soft-work-invalidations.md deleted file mode 100644 index a676a35ba..000000000 --- a/.changeset/soft-work-invalidations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Allow query consumers to opt into non-blocking invalidation refetches. diff --git a/.changeset/sour-turtles-smash.md b/.changeset/sour-turtles-smash.md deleted file mode 100644 index 95c1247bc..000000000 --- a/.changeset/sour-turtles-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -bs diff --git a/.changeset/source-first-package-exports.md b/.changeset/source-first-package-exports.md deleted file mode 100644 index 3ea66b9db..000000000 --- a/.changeset/source-first-package-exports.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Point development package exports at TypeScript sources while keeping published exports on compiled dist files. diff --git a/.changeset/sparkly-cars-press.md b/.changeset/sparkly-cars-press.md deleted file mode 100644 index c8d97eaa6..000000000 --- a/.changeset/sparkly-cars-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fixes useOmegaFormErrorStale stale errorMap diff --git a/.changeset/spicy-falcons-rush.md b/.changeset/spicy-falcons-rush.md deleted file mode 100644 index b82ea51a4..000000000 --- a/.changeset/spicy-falcons-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -improve branded types, keep types through Rebuild diff --git a/.changeset/spicy-lights-trade.md b/.changeset/spicy-lights-trade.md deleted file mode 100644 index 0823fd9b8..000000000 --- a/.changeset/spicy-lights-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -fix: double guard diff --git a/.changeset/sql-namespace-support.md b/.changeset/sql-namespace-support.md deleted file mode 100644 index 5116373fe..000000000 --- a/.changeset/sql-namespace-support.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add storage namespace support to SQL adapters (SQLite and Postgres) via a `_namespace` column. When `allowNamespace` is configured, a `_namespace` column with composite primary key `(id, _namespace)` isolates data per namespace within the same table. New tables get the schema automatically; existing tables require manual migration. diff --git a/.changeset/sql-store-adapter.md b/.changeset/sql-store-adapter.md deleted file mode 100644 index 07d3cfb6f..000000000 --- a/.changeset/sql-store-adapter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add SQL Store adapter for Effect SQL (SQLite + PostgreSQL). Table-per-repo with id/etag/data JSON columns, query DSL translation to SQL WHERE clauses, optimistic concurrency via etag. diff --git a/.changeset/sql-transaction-middleware.md b/.changeset/sql-transaction-middleware.md deleted file mode 100644 index 55a89784e..000000000 --- a/.changeset/sql-transaction-middleware.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Make `withSqlTransaction` in `setupRequest` configurable via `withTransaction` option (defaults to `false`). Add `requiresTransactionConfig` and `makeSqlTransactionMiddleware` for per-RPC transaction control as a dynamic middleware that requires `SqlClient` directly. Transactions are disabled by default; opt in with `withTransaction: true` or `requiresTransaction: true`. diff --git a/.changeset/sqlite-per-namespace-layermap.md b/.changeset/sqlite-per-namespace-layermap.md deleted file mode 100644 index 0d2a39a6e..000000000 --- a/.changeset/sqlite-per-namespace-layermap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add per-namespace in-memory SQLite adapter via LayerMap. When `makeSqlClientLayer` option is provided to `SQLiteStoreLayer`, each namespace gets its own SQLite database instance managed by a `LayerMap`. Introduces `WithNsTransaction` service for namespace-aware SQL transactions, used by `makeSqlTransactionMiddleware`. diff --git a/.changeset/sqlite-workflow-engine.md b/.changeset/sqlite-workflow-engine.md deleted file mode 100644 index 87df36154..000000000 --- a/.changeset/sqlite-workflow-engine.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": minor ---- - -Add SQLite backed `WorkflowEngine` adapter (`layerSqlite` in `WorkflowEngineSqlite.ts`). Persists workflow state across `workflow_exec` / `workflow_activity` / `workflow_deferred` / `workflow_clock` tables via `SqlClient`. Uses `sql.withTransaction` for atomicity, etag-based optimistic concurrency (`UPDATE ... WHERE etag = ? RETURNING etag`), and `INSERT ... ON CONFLICT DO NOTHING RETURNING` for first-writer-wins on activity / deferred / clock writes. Values crossing the storage boundary round-trip through `Schema` codecs (`S.fromJsonString(S.toCodecJson(...))`) using the workflow's own `payloadSchema` / `successSchema` / `errorSchema` for typed values, and the cluster engine's opaque `AnyOrVoid` codec for activity / deferred payloads. Includes time-bound lease + heartbeat fiber, scope-bound recovery poller for crashed-driver takeover, and cross-partition clock poller for restart-survivable durable timers. diff --git a/.changeset/sse-context-map-scope.md b/.changeset/sse-context-map-scope.md deleted file mode 100644 index dc1af8b99..000000000 --- a/.changeset/sse-context-map-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Fix ContextMap finalizer running mid-stream on SSE responses. SSE handler now binds ContextMapContainer to the request scope via a shared `provideOnRequestScope` helper (also used by `RequestContextMiddleware`), so finalizers only run after the response body is fully drained. Adds `setupStreamingRequestContextFromCurrent` for use by streaming HTTP handlers. diff --git a/.changeset/sse-filter-namespace-by-storeid.md b/.changeset/sse-filter-namespace-by-storeid.md deleted file mode 100644 index 23e962f37..000000000 --- a/.changeset/sse-filter-namespace-by-storeid.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Filter SSE events stream by storeId namespace diff --git a/.changeset/staged-publish-directory.md b/.changeset/staged-publish-directory.md deleted file mode 100644 index f83a5a89d..000000000 --- a/.changeset/staged-publish-directory.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/cli": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"effect-app": patch ---- - -Publish from generated staging directories so release-only files are created outside the source package tree. diff --git a/.changeset/stale-actors-remain.md b/.changeset/stale-actors-remain.md deleted file mode 100644 index 7f3fbe91d..000000000 --- a/.changeset/stale-actors-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Handles blur in veutify diff --git a/.changeset/stale-emus-see.md b/.changeset/stale-emus-see.md deleted file mode 100644 index 54d0958af..000000000 --- a/.changeset/stale-emus-see.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fixes Redacted Meta in Omega Form diff --git a/.changeset/stale-papers-join.md b/.changeset/stale-papers-join.md deleted file mode 100644 index 1133db65e..000000000 --- a/.changeset/stale-papers-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -improve: class strictness enabled by default again, allow `strict: false` as opt out for now. diff --git a/.changeset/store-decode-schema-error.md b/.changeset/store-decode-schema-error.md deleted file mode 100644 index 991a22862..000000000 --- a/.changeset/store-decode-schema-error.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -Store document decode fails as a typed `SchemaError` instead of a defect; repositories keep their public error channels by dying at the boundary; `validateSample` reports documents that fail at the store boundary. diff --git a/.changeset/store-effect-cached.md b/.changeset/store-effect-cached.md deleted file mode 100644 index 4e2b6dc9f..000000000 --- a/.changeset/store-effect-cached.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Use `Effect.cached` in `getOrCreateStoreEffect` for proper memoization of the resolver effect. diff --git a/.changeset/store-json-values.md b/.changeset/store-json-values.md deleted file mode 100644 index 03fb1280e..000000000 --- a/.changeset/store-json-values.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -JSON stores lower native Encoded values (Date, Map, Set, and app types such as DateOnly) through the store's document schema. - -`makeRepo` already passes that schema. Adapters encode documents, query parameters, and defaults with `Schema.toCodecJson(toEncoded(schema))` at the field path. No type registry. Schemaless stores still lower Date/Map/Set structurally. diff --git a/.changeset/store-seed-uninterruptible.md b/.changeset/store-seed-uninterruptible.md deleted file mode 100644 index a41808100..000000000 --- a/.changeset/store-seed-uninterruptible.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Wrap store seed effects with `Effect.uninterruptible` to prevent interruption during seeding diff --git a/.changeset/stream-callable-progress.md b/.changeset/stream-callable-progress.md deleted file mode 100644 index ed672e2b7..000000000 --- a/.changeset/stream-callable-progress.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/vue": minor -"@effect-app/vue-components": minor ---- - -Refine `mutateStream` shape and progress reporting. - -- `mutateStream(options?)` now returns the `execute` callable directly, with `id`, `running?`, and `progress?` attached as properties. Tuple form `[ref, execute]` is gone — invoke the callable to run the stream, or pass it (or the factory) to `Command.fn` / `Command.wrap` / `Command.wrapStream`. -- `progress` formatter return type widened from `string | undefined` to `Progress | undefined`, where `Progress = string | { text: string; percentage: number }`. -- Stream failures now bubble through the execute effect's typed error channel `E` instead of being swallowed. The reactive `AsyncResult` ref still mirrors the failure for live progress UI. -- `CommandBase.progress?: Progress` replaces `progressText?: string`. `CommandButton` overrides the Vuetify `loader` slot when `progress` is set, rendering a `v-progress-circular` (bound to `model-value` when a `percentage` is supplied, otherwise `indeterminate`) alongside the formatted text. -- Factories and callables are branded with `_streamFactory` / `_streamCallable` so `Command.fn` / `Command.wrap` can disambiguate them from plain mutate functions. diff --git a/.changeset/stream-drain-data-dependencies.md b/.changeset/stream-drain-data-dependencies.md deleted file mode 100644 index fc6d8ab43..000000000 --- a/.changeset/stream-drain-data-dependencies.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Stream RPC: drain read/write data-dependencies per emitted value chunk instead of sending the -cumulative set on every metadata chunk. Each metadata chunk now carries only the delta recorded -since the last chunk, the bucket is cleared for the next segment, and the terminal "done"/"error" -chunks drain the remainder. The emit condition also broadens to include non-empty reads, so stream -queries forward their read-dependencies mid-stream too. The client already accumulates deltas into -its per-call recorder, so no FE change is required. diff --git a/.changeset/stream-effect-fail.md b/.changeset/stream-effect-fail.md deleted file mode 100644 index 6ca924187..000000000 --- a/.changeset/stream-effect-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Stream resources accept `Effect.fail(...)` (and `Effect`) from controller handlers — previously only `Stream.fail(...)` / a returned `Stream` worked. The router now lifts an Effect result to a Stream via `Stream.unwrap`, so failures from an `Effect` propagate as a failing Stream on the client, matching `Stream.fail(...)` behavior. Also removes the need for manual `.pipe(Stream.unwrap)` on generator handlers that return a `Stream`. diff --git a/.changeset/stream-final-default-success.md b/.changeset/stream-final-default-success.md deleted file mode 100644 index 917afbf09..000000000 --- a/.changeset/stream-final-default-success.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Default the stream `mutateStream` execute resolved value to the request's success type when no `final` schema is declared. - -Previously the type defaulted to `void`, but the runtime already resolves with the last emitted value. Types now match runtime behaviour: `execute` returns `Final` if a `final` schema is set, otherwise the success type. diff --git a/.changeset/stream-final-type.md b/.changeset/stream-final-type.md deleted file mode 100644 index 3d6982831..000000000 --- a/.changeset/stream-final-type.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@effect-app/vue": minor -"effect-app": minor ---- - -Stream requests now support an optional `final` schema that models the final success type of the stream. When declared, `mutateStream`'s execute effect resolves with the last emitted value typed as `Final` instead of `void`. - -```ts -class MyStream - extends SomethingStream()("MyStream", { id: S.String }, { - success: S.Union([OperationProgress, ExportComplete]), - final: ExportComplete // execute now resolves with ExportComplete - }) -{} -``` diff --git a/.changeset/stream-first-write-flush.md b/.changeset/stream-first-write-flush.md deleted file mode 100644 index 25591fe83..000000000 --- a/.changeset/stream-first-write-flush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Flush stream mutation write-deps once when the first write arrives, then again on settlement. Long-running streams can refresh queries like GetActiveJob without invalidating list queries on every subsequent item write. diff --git a/.changeset/stream-fn-enhancements.md b/.changeset/stream-fn-enhancements.md deleted file mode 100644 index 0a156647a..000000000 --- a/.changeset/stream-fn-enhancements.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/vue": minor -"@effect-app/vue-components": minor ---- - -- `CommandButton`: add optional `:map-progress` prop to compute progress from `command.result` via a custom mapper function -- `CommandBase`: add optional `result` field exposing reactive `AsyncResult` state -- Export `Progress` type from `@effect-app/vue` -- `streamFn`: pipe operators now receive the initial `Effect` (or `Stream`) value unchanged; `Stream.unwrap` is deferred until after all combinators, enabling use of `withDefaultToast` and other Effect-level combinators -- Add `makeStreamMutation2`: like `makeStreamMutation` but returns `Effect` per invocation (with invalidation via `Stream.ensuring`), for use with `streamFn` combinators -- Expose `streamFn` on `XClient.Y` stream handlers and on the `Command` object -- Expose `mutateStream2` on `XClient.Y` stream handlers, with a `wrapStream` helper that calls `streamFn` with the handler and provided combinators diff --git a/.changeset/stream-metadata-v1.md b/.changeset/stream-metadata-v1.md deleted file mode 100644 index cc4d2ed44..000000000 --- a/.changeset/stream-metadata-v1.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor -"@effect-app/vue": minor ---- - -V1/V2/V3: stream and command requests carry invalidation metadata - -**V1** – stream final response includes metadata - -- `Invalidation.StreamResponseChunk` wraps each stream item as `{ _tag: "value", value }` and appends `{ _tag: "done", metadata }` at the end carrying all accumulated invalidation keys. - -**V2** – invalidation keys included in failures - -- `Invalidation.CommandFailureWithMetaData` and `Invalidation.StreamFailureChunk` carry keys accumulated up to the point of failure, so clients can invalidate queries even when a command or stream errors. -- `InvalidationMiddlewareLive` wraps command failures; `routing.ts` wraps stream failures. -- `apiClientFactory.ts` unwraps both on the client side, forwarding keys before re-failing with the original error. - -**V3** – mid-stream metadata chunks - -- `Invalidation.StreamResponseChunk` now also includes `{ _tag: "metadata", metadata }` for mid-stream invalidation. -- After each emitted value, the server drains accumulated keys and emits a "metadata" chunk if any keys were collected since the last drain (bucket reset via `InvalidationSet.drain`). -- `apiClientFactory.ts` processes "metadata" chunks the same as "done" chunks, forwarding keys to `InvalidationKeysFromServer` immediately. -- `makeInvalidationKeysService` accepts an optional `onAdded` callback that fires after each key addition, enabling `mutate.ts` to trigger query invalidation mid-stream without waiting for the stream to complete. diff --git a/.changeset/stream-on-command-query.md b/.changeset/stream-on-command-query.md deleted file mode 100644 index c9965b550..000000000 --- a/.changeset/stream-on-command-query.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor -"@effect-app/vue": minor ---- - -Add streaming as a `stream: true` config option on `Query` / `Command` instead of a separate request type. - -`TaggedRequestFor` now exposes only `Query` and `Command` factories — the standalone `Stream` factory is removed. To produce a Stream of `success` values, pass `stream: true` in the request config. The request `type` field stays `"command" | "query"`; a new `stream: boolean` field carries the streaming flag (stripped from the stored handler config). - -```ts -// Query that streams results -Req.Query()("Tag", {}, { stream: true, success: ... }) - -// Command that streams results -Req.Command()("Tag", {}, { stream: true, success: ... }) -``` - -Vue client mapping (per-handler properties mirror the non-stream API — `.query`, `.fn`, `.mutate`): - -- `query` + `stream: true` → exposes `.query` (read-only streaming, tracked Vue Query). Helper map key: `${name}Query`. -- `command` + `stream: true` → exposes `.fn` and `.mutate` (mutating streaming). -- Plain `query` / `command` unchanged. - -Server routing dispatches via the new `stream` flag (`makeStreamRpc` for streaming commands/queries, `makeCommandRpc` / `Rpc.make` otherwise). - -Also lifts the `Struct` / `TaggedStruct` and `Opaque` definitions in `effect-app/Schema` to use `S.Bottom` / `S.Opaque` directly, exposing `fields`, `mapFields`, and a `MakeIn` that allows `void` when all fields are optional. `TaggedRequestFor` request classes now use `Opaque(TaggedStruct(...))` instead of `TaggedClass`, and decoding/encoding services are derived from `success` / `error` rather than stored on the request. - -**Migration**: replace `Req.Stream` with `Req.Query` or `Req.Command` and add `stream: true` to the config — `Query` for read-only streams, `Command` for mutating streams. diff --git a/.changeset/stream-withdefaulttoast.md b/.changeset/stream-withdefaulttoast.md deleted file mode 100644 index 7541c0c7c..000000000 --- a/.changeset/stream-withdefaulttoast.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue": minor -"@effect-app/vue-components": patch ---- - -Add `Command.withDefaultToastStream` — a stream-aware combinator for `streamFn` that properly handles the full stream lifecycle (waiting/success/failure toasts). Unlike `withDefaultToast`, it waits for the stream to drain before showing the success toast and correctly handles stream errors. - -Strongly type `CommandBase` with `RA`/`RE` type params for `result`, and update `CommandButton`'s `mapProgress` prop to be typed as `(result: AsyncResult) => Progress | undefined`. diff --git a/.changeset/strict-stream-handlers.md b/.changeset/strict-stream-handlers.md deleted file mode 100644 index 2c26607ac..000000000 --- a/.changeset/strict-stream-handlers.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Router handlers are now discriminated by `Resource[K]["stream"]`: - -- Stream resources (`stream: true`) accept only `(req) => Stream<...>` handlers. -- Non-stream resources accept only `(req) => Effect<...>` (or generator yielding `Yieldable`). - -Mixing — e.g. returning `Effect.fail(...)` or `Effect>` from a stream handler — no longer type-checks. - -The runtime `Stream.unwrap` branch that lifted `Effect`/`Effect` returns into a `Stream` is removed; handlers for stream resources must return a `Stream` directly. Migrate `Effect.gen(...).pipe(Stream.unwrap)` patterns by returning the `Stream` directly, and convert `Effect.fail(err)` in stream handlers to `Stream.fail(err)`. diff --git a/.changeset/stringid-native-arbitrary.md b/.changeset/stringid-native-arbitrary.md deleted file mode 100644 index b35d75a23..000000000 --- a/.changeset/stringid-native-arbitrary.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Restore native Arbitrary for the schemas that lost custom fast-check `toArbitrary`. `StringId` uses `toCodecArbitrary` (the native replacement): generate a 210-byte `Uint8Array` and run `customRandom(urlAlphabet, 21, …)` — same as the old `StringIdArb`. JSON stays a branded string via `toCodec`. `Url` is `https://…`. `RequestId` samples unique nanoid-shaped ids. `Finite` generation is capped at ±1e6. `generateFromSchema` jumps its master seed per call so successive `count: 1` draws do not collide on attempt-0 edge strings. diff --git a/.changeset/strong-dots-sin.md b/.changeset/strong-dots-sin.md deleted file mode 100644 index d65818673..000000000 --- a/.changeset/strong-dots-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -fix union default value diff --git a/.changeset/struct-pick-omit.md b/.changeset/struct-pick-omit.md deleted file mode 100644 index f6e0b54b4..000000000 --- a/.changeset/struct-pick-omit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": minor ---- - -Add pick/omit to Struct and TaggedStruct diff --git a/.changeset/structfacade-effect-app-struct.md b/.changeset/structfacade-effect-app-struct.md deleted file mode 100644 index 7ef3229b3..000000000 --- a/.changeset/structfacade-effect-app-struct.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch ---- - -Fix `StructFacade` to extend effect-app's own `Struct`, not `effect/Schema`'s. - -`StructFacade` was built on `Omit, ...>` where `S` is `effect/Schema` (core). Scanner / consumer models are effect-app `S.Struct`, which is a distinct type from effect core's `Struct`, so a facade built on the core `Struct` is not assignable where an effect-app struct is expected — e.g. workflow payloads (`Workflow<…, Struct>`) and other effect-app `Struct`-shaped positions failed to type-check. Switch the `Omit` base to effect-app's `Struct` (the `Fields extends S.Struct.Fields` constraint stays effect core, matching effect-app's own `Struct`). diff --git a/.changeset/suspense-keep-previous-value.md b/.changeset/suspense-keep-previous-value.md deleted file mode 100644 index 84c915103..000000000 --- a/.changeset/suspense-keep-previous-value.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Keep the previous suspense value across reactive-arg atom switches. - -A reactive arg re-points a resolved suspense query at a different family atom; the fresh atom starts `Initial` (no `previousSuccess` carried over), so the always-defined `data` computed found `undefined` and threw `Internal Error: suspense resolved without a latest value` during the next render flush. The always-defined ref now serves the last defined value across that transition (TanStack's keepPreviousData — Vue cannot re-suspend after mount); waiting/failure of the new fetch stays observable on the `result` ref. Applied to `.suspense()`, `.suspenseNew()` and `useAtomSuspense` via a shared `latestDefined` helper. It only throws when there has never been a value, which is unreachable once the suspense await resolved. diff --git a/.changeset/sweet-humans-ring.md b/.changeset/sweet-humans-ring.md deleted file mode 100644 index 21b00a4f3..000000000 --- a/.changeset/sweet-humans-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix signature diff --git a/.changeset/sweet-moments-see.md b/.changeset/sweet-moments-see.md deleted file mode 100644 index 5bd173c53..000000000 --- a/.changeset/sweet-moments-see.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -restore annotations for now diff --git a/.changeset/swift-owls-knit.md b/.changeset/swift-owls-knit.md deleted file mode 100644 index e7e836c0e..000000000 --- a/.changeset/swift-owls-knit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Add relaxed wrapper support for `ErrorClass` and `TaggedErrorClass` in `effect-app/Schema`, matching the existing class wrapper behavior (`copy`, cached `ast`, and unbounded parse concurrency). diff --git a/.changeset/swift-tables-turn.md b/.changeset/swift-tables-turn.md deleted file mode 100644 index 48772d49e..000000000 --- a/.changeset/swift-tables-turn.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -update packages diff --git a/.changeset/sync-effect-app-command.md b/.changeset/sync-effect-app-command.md deleted file mode 100644 index 50d53bed7..000000000 --- a/.changeset/sync-effect-app-command.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Add `sync-effect-app` to sync vendored Effect App libs by pinned package version. -The command prefers package-version git tags and supports `--ref latest` for main. diff --git a/.changeset/sync-effect-subtree.md b/.changeset/sync-effect-subtree.md deleted file mode 100644 index 97711f395..000000000 --- a/.changeset/sync-effect-subtree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": minor ---- - -Add `sync-effect` CLI command to sync the Effect git subtree to the version pinned in package.json manifests. diff --git a/.changeset/sync-effect-url.md b/.changeset/sync-effect-url.md deleted file mode 100644 index a41630070..000000000 --- a/.changeset/sync-effect-url.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -Sync Effect subtrees from a repository URL without requiring a local git remote. diff --git a/.changeset/sync-push-deletions.md b/.changeset/sync-push-deletions.md deleted file mode 100644 index dd5e8c602..000000000 --- a/.changeset/sync-push-deletions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/cli": patch ---- - -`effa sync-push`: propagate locally-deleted synced files. Previously, files removed from the consuming project were silently skipped (the loop bailed when `destAbs` didn't exist). Now they're recorded as deletions, `git rm`'d in the cache branch, and listed in the PR body alongside modifications. diff --git a/.changeset/synchronous-change-feed.md b/.changeset/synchronous-change-feed.md deleted file mode 100644 index d8d0f3100..000000000 --- a/.changeset/synchronous-change-feed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Repository `changeFeed` now broadcasts synchronously: `publish` awaits every subscribed handler before resolving. Replaces `PubSub.PubSub` with a `ChangeFeed` interface (`publish` + scoped `subscribe`). Handlers are auto-removed when the subscriber's scope closes, and the full handler set is cleared when the repository's scope closes. Repository construction now requires `Scope` — wire `makeRepo` through `Layer.scoped` / `Effect.scoped`. diff --git a/.changeset/tagged-request-command-query-split.md b/.changeset/tagged-request-command-query-split.md deleted file mode 100644 index 385aa6bc5..000000000 --- a/.changeset/tagged-request-command-query-split.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Split `TaggedRequestFor` into `Query` and `Command` factories, and mark generated request classes with `type: "query" | "command"`. - -Vue client helpers now expose query-only helpers (`query`, `suspense`, `fetch`) for query requests and mutation-only helpers (`mutate`, `fetch`) for command requests. diff --git a/.changeset/tagged-request-for.md b/.changeset/tagged-request-for.md deleted file mode 100644 index 923c9ecb0..000000000 --- a/.changeset/tagged-request-for.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/eslint-codegen-model": minor ---- - -Add `TaggedRequestFor` helper to `makeRpcClient` that curries a `moduleName`, producing request classes with static `id` and `moduleName` properties. This enables passing request classes directly to `makeQueryKey` without going through `clientFor` first. The `clientFor` function no longer requires a `meta` property on the module when requests carry `moduleName`. The meta codegen preset now generates `Req = TaggedRequestFor(moduleName)`. Original `TaggedRequest` remains for backwards compatibility. diff --git a/.changeset/tagged-union-drop-fields-constraint.md b/.changeset/tagged-union-drop-fields-constraint.md deleted file mode 100644 index 2810c05d5..000000000 --- a/.changeset/tagged-union-drop-fields-constraint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -remove `fields` constraint from TaggedUnion/tags helpers, extract `_tag` via AST walk instead diff --git a/.changeset/tagged-union-generate-guards.md b/.changeset/tagged-union-generate-guards.md deleted file mode 100644 index 42c681dc9..000000000 --- a/.changeset/tagged-union-generate-guards.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect-app": minor ---- - -Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - -- `generateGuards("key")` — generic per-guard, no need to specify the container type -- `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - -Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. diff --git a/.changeset/tagged-union-generate-guards2.md b/.changeset/tagged-union-generate-guards2.md deleted file mode 100644 index 42c681dc9..000000000 --- a/.changeset/tagged-union-generate-guards2.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect-app": minor ---- - -Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - -- `generateGuards("key")` — generic per-guard, no need to specify the container type -- `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - -Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. diff --git a/.changeset/tall-boxes-relax.md b/.changeset/tall-boxes-relax.md deleted file mode 100644 index 152676792..000000000 --- a/.changeset/tall-boxes-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -improve sentry diff --git a/.changeset/tall-hats-reflect.md b/.changeset/tall-hats-reflect.md deleted file mode 100644 index 2d1e31311..000000000 --- a/.changeset/tall-hats-reflect.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Refactor command invalidation typing: declare resources via `Command()`, pass `invalidatesQueries` as the optional 4th argument, and enforce exact `clientFor` invalidation resources when required. diff --git a/.changeset/tanstack-effect-equal-structural-sharing.md b/.changeset/tanstack-effect-equal-structural-sharing.md deleted file mode 100644 index 342413c8b..000000000 --- a/.changeset/tanstack-effect-equal-structural-sharing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Default legacy TanStack queries to Effect-Equal-aware structural sharing. diff --git a/.changeset/tanstack-suspense-seed.md b/.changeset/tanstack-suspense-seed.md deleted file mode 100644 index 716d9ce57..000000000 --- a/.changeset/tanstack-suspense-seed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Seed legacy TanStack suspense results into Vue query refs when the observer re-points mid-flight. diff --git a/.changeset/tasty-eggs-stare.md b/.changeset/tasty-eggs-stare.md deleted file mode 100644 index 45ecad131..000000000 --- a/.changeset/tasty-eggs-stare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -fix: nasty request scope bug diff --git a/.changeset/tasty-fans-accept.md b/.changeset/tasty-fans-accept.md deleted file mode 100644 index d323c56ce..000000000 --- a/.changeset/tasty-fans-accept.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Configure Changesets fixed versioning for public packages. diff --git a/.changeset/ten-crews-like.md b/.changeset/ten-crews-like.md deleted file mode 100644 index b24acbc35..000000000 --- a/.changeset/ten-crews-like.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -remove artificial Cosmos bulkSet delays diff --git a/.changeset/ten-suns-shine.md b/.changeset/ten-suns-shine.md deleted file mode 100644 index e17884c8d..000000000 --- a/.changeset/ten-suns-shine.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Relax invalidation resource value constraints to allow arbitrary values while preserving query-only filtering in invalidation handling. diff --git a/.changeset/tender-bars-ring.md b/.changeset/tender-bars-ring.md deleted file mode 100644 index 9d864bf1c..000000000 --- a/.changeset/tender-bars-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -fml diff --git a/.changeset/tender-donkeys-pick.md b/.changeset/tender-donkeys-pick.md deleted file mode 100644 index 919ed956d..000000000 --- a/.changeset/tender-donkeys-pick.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -pass options diff --git a/.changeset/thin-zebras-press.md b/.changeset/thin-zebras-press.md deleted file mode 100644 index c73c5fb1a..000000000 --- a/.changeset/thin-zebras-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Fix `FixedNuxtErrorBoundary` losing the app's router: `vue-router` was missing from `vite.config.mts`'s Rollup `external` list, so it got bundled into the library output with its own `Symbol("router")` injection key a different instance from the host app's real `routerKey`. `useRouter()` inside the component therefore always returned `undefined`, crashing on `.afterEach` (500 in SSR). `vue-router` is now external like `vue`, so the component resolves the host app's actual router instance. diff --git a/.changeset/thirty-cooks-pump.md b/.changeset/thirty-cooks-pump.md deleted file mode 100644 index ae711deb3..000000000 --- a/.changeset/thirty-cooks-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -enable cosmosdb large partition key hashes diff --git a/.changeset/thirty-experts-taste.md b/.changeset/thirty-experts-taste.md deleted file mode 100644 index 7de73d635..000000000 --- a/.changeset/thirty-experts-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -rename Literal to Literals, no longer hiding Schema's singular Literal. diff --git a/.changeset/three-hotels-roll.md b/.changeset/three-hotels-roll.md deleted file mode 100644 index 772776820..000000000 --- a/.changeset/three-hotels-roll.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -cleanup diff --git a/.changeset/tidy-caches-reflect.md b/.changeset/tidy-caches-reflect.md deleted file mode 100644 index 9f5080a94..000000000 --- a/.changeset/tidy-caches-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Route `useUpdateQuery` through the configured query cache so TanStack-backed legacy queries receive manual cache updates. diff --git a/.changeset/tidy-cosmos-batches.md b/.changeset/tidy-cosmos-batches.md deleted file mode 100644 index f54f2eaab..000000000 --- a/.changeset/tidy-cosmos-batches.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Batch more Cosmos cluster storage updates. diff --git a/.changeset/tidy-derived-dependencies.md b/.changeset/tidy-derived-dependencies.md deleted file mode 100644 index 617b9570d..000000000 --- a/.changeset/tidy-derived-dependencies.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": patch ---- - -Support ID-scoped signal dependencies and additive repository dependencies derived from previous and current entities. diff --git a/.changeset/tidy-dprint-oxlint-config.md b/.changeset/tidy-dprint-oxlint-config.md deleted file mode 100644 index 89b9d6708..000000000 --- a/.changeset/tidy-dprint-oxlint-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -ship shared dprint config and wire oxlint and dprint into package lint scripts diff --git a/.changeset/tidy-effects-diagnose.md b/.changeset/tidy-effects-diagnose.md deleted file mode 100644 index 2d6799345..000000000 --- a/.changeset/tidy-effects-diagnose.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-shared-config": patch ---- - -Run configured Effect diagnostics through Oxlint's type-aware lint integration. diff --git a/.changeset/tidy-forms-report.md b/.changeset/tidy-forms-report.md deleted file mode 100644 index b4d1a58d7..000000000 --- a/.changeset/tidy-forms-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Handle string form validation errors in OmegaForm.Errors without crashing, and safely ignore unsupported error values. diff --git a/.changeset/tidy-pans-rule.md b/.changeset/tidy-pans-rule.md deleted file mode 100644 index 09c873d78..000000000 --- a/.changeset/tidy-pans-rule.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it diff --git a/.changeset/tidy-poets-think.md b/.changeset/tidy-poets-think.md deleted file mode 100644 index ff5f43093..000000000 --- a/.changeset/tidy-poets-think.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -update effect to latest beta diff --git a/.changeset/tidy-remaining-formatting.md b/.changeset/tidy-remaining-formatting.md deleted file mode 100644 index c267cb06a..000000000 --- a/.changeset/tidy-remaining-formatting.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -apply the remaining formatting and autofix cleanup across the vue packages diff --git a/.changeset/tidy-rivers-identify.md b/.changeset/tidy-rivers-identify.md deleted file mode 100644 index a9556c590..000000000 --- a/.changeset/tidy-rivers-identify.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Include changed entity IDs in repository data dependencies, with explicit query scopes and configurable write aliases, while retaining coarse collection dependencies. diff --git a/.changeset/tidy-titles-remove.md b/.changeset/tidy-titles-remove.md deleted file mode 100644 index a028982b0..000000000 --- a/.changeset/tidy-titles-remove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Remove redundant schema `title` annotations when they duplicate the schema `identifier`. diff --git a/.changeset/tiny-cows-repair.md b/.changeset/tiny-cows-repair.md deleted file mode 100644 index 92390d4e5..000000000 --- a/.changeset/tiny-cows-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -Preserve class annotation parseOptions in relaxed declaration struct decoding so custom parse options (including concurrency defaults) are applied consistently. diff --git a/.changeset/tiny-gorillas-jump.md b/.changeset/tiny-gorillas-jump.md deleted file mode 100644 index f4af4a259..000000000 --- a/.changeset/tiny-gorillas-jump.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch ---- - -Rename the Class/TaggedClass relaxed declaration option to `strict` (default `false`) and apply it to `Class`, `TaggedClass`, `ExtendedClass`, and `ExtendedTaggedClass`. - -When `strict: true`, class decoding keeps strict class-level declaration behavior; by default, decoding remains relaxed and preserves field-level schema errors. diff --git a/.changeset/tired-insects-stand.md b/.changeset/tired-insects-stand.md deleted file mode 100644 index 2f3526345..000000000 --- a/.changeset/tired-insects-stand.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -align with earlier updates diff --git a/.changeset/toast-group-request-id.md b/.changeset/toast-group-request-id.md deleted file mode 100644 index dd2574c67..000000000 --- a/.changeset/toast-group-request-id.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add optional `groupId` and `requestId` to toast options. - -- `ToastOpts` / `ToastOptsInternal` now accept `groupId?: string` and `requestId?: string`; values pass through the toast wrapper unchanged. -- `WithToast` / `withToast` accept `groupId` and auto-derive `requestId` once per invocation (current Effect span `traceId`, fallback `S.StringId.make()`); both are attached to every emitted toast (waiting/success/warning/error). -- `Command.withDefaultToast` and `Command.withDefaultToastStream` set `groupId: cc.id` automatically; the stream variant also computes `requestId` once and threads it through progress, success, and failure toasts. diff --git a/.changeset/tough-apes-whisper.md b/.changeset/tough-apes-whisper.md deleted file mode 100644 index ea97c1544..000000000 --- a/.changeset/tough-apes-whisper.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Fix `TaggedRequest` no-config error type inference so requests without a third argument infer the same default error schema as requests with explicit success config. diff --git a/.changeset/tsgo-decodingservices-workaround.md b/.changeset/tsgo-decodingservices-workaround.md deleted file mode 100644 index de731817e..000000000 --- a/.changeset/tsgo-decodingservices-workaround.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/vue": patch ---- - -Work around tsgo failing to reduce `S.Codec.DecodingServices` (`X extends Top ? X["DecodingServices"] : never`) in generic positions, which left `unknown` and polluted the `R` channel of client handlers and RPC middleware failure context. Since the schemas involved are already constrained to `S.Top`, read `["DecodingServices"]` directly in `RequestHandlerFor`, `TagClass.FailureContext`, and the Vue `MutationExt` / `QueryProjection` types. diff --git a/.changeset/tsgo-direct-schema-property-access.md b/.changeset/tsgo-direct-schema-property-access.md deleted file mode 100644 index 1a2c35b49..000000000 --- a/.changeset/tsgo-direct-schema-property-access.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -improve tsgo compat: avoid deferred `Schema.Type`/`Codec.Encoded`/`Codec.DecodingServices`/`Codec.EncodingServices` conditional helpers in generic positions where the type parameter is already constrained to `Schema.Top`. Index the property directly (`X["Type"]`, `X["Encoded"]`, `X["DecodingServices"]`, …) so tsgo doesn't leak `unknown` into `Effect` channels (notably `R`). - -Sites: `client/clientFor.ts` (`RequestHandlerFor`, `FinalTypeOf`, `ExtractResponse`, `ExtractEResponse`), `client/makeClient.ts` (`InputFromPayload`, `OutputFromSuccess`, `InvalidationConfigForCommand`, `TaggedRequestWithMeta` overloads), `rpc/MiddlewareMaker.ts` (`Errors`), `rpc/RpcMiddleware.ts` (`Failure`, `FailureContext`), `Schema/ext.ts` (`ReadonlySetFromArray`, `ReadonlyMapFromArray`), `infra/routing.ts` (`GetSuccessShape`, handlers, route matcher), `vue/makeClient.ts` (`MutationExt.project`, `MutationWithExtensions`, `QueryProjection`), `vue/routeParams.ts` (`parseRouteParams*`). diff --git a/.changeset/twenty-seas-grab.md b/.changeset/twenty-seas-grab.md deleted file mode 100644 index cd235a34a..000000000 --- a/.changeset/twenty-seas-grab.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"effect-app": patch -"@effect-app/cli": patch ---- - -update all teh tings diff --git a/.changeset/two-clocks-wash.md b/.changeset/two-clocks-wash.md deleted file mode 100644 index 48772d49e..000000000 --- a/.changeset/two-clocks-wash.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect-app/cli": patch -"effect-app": patch -"@effect-app/eslint-codegen-model": patch -"@effect-app/eslint-shared-config": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -update packages diff --git a/.changeset/typed-database-error.md b/.changeset/typed-database-error.md deleted file mode 100644 index 7d9d0dee7..000000000 --- a/.changeset/typed-database-error.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect-app": minor -"@effect-app/infra": minor ---- - -Add a typed, serializable `DatabaseError { message, transient, cause }` for store adapter failures. - -Store adapters wrapped DB calls in bare `Effect.promise` / `.orDie`, so a transient infra failure (request timeout, throttle, 5xx, dropped socket) became a raw-`Error` defect — which `Effect.retry` can't retry and which breaks JSON encoding of a workflow exit cause ("Expected JSON value, got Error"). `DatabaseError` is now exposed on all `Store` and `Repository` method error channels (writes: `OptimisticConcurrencyException | DatabaseError`) and added to `SupportedErrors` so the api/client/FE treat it as a 500-class error. Each adapter wraps its db-call failures into `DatabaseError` with a `transient` flag (timeout/throttle/5xx ⇒ retryable); the `cause` serializes via `Schema.Defect`. Construction/seed/DDL paths stay `orDie`. diff --git a/.changeset/typed-query-builders.md b/.changeset/typed-query-builders.md deleted file mode 100644 index 0e3da2289..000000000 --- a/.changeset/typed-query-builders.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect-app": minor ---- - -Tighten typing of query DSL builders so field/path parameters are constrained to actual field paths instead of bare `string`: - -- `relation(path)` now infers the relation's element type and constrains `distinctCount`/`sum`/`collect`/`collectDistinct`/`collectFields`/`collectDistinctFields` and the `unit` of `sumExprBy`/`sumExprNormalized` to `FieldPath`. -- `relation(path).expr` exposes a scope-bound math-expression builder so `expr.field(...)` inside `sumExpr`/`sumExprBy`/`sumExprNormalized` is typed against the relation element. -- Top-level `expr.field` accepts an optional generic for opt-in tightening (`expr.field("x")`). -- `aggregate(schema, build)` accepts a builder callback whose `agg` argument is bound to the source row inferred from the pipe — paths are checked without any explicit generic: `make().pipe(aggregate(schema, ($) => ({ city: $.field("address.city") })))`. Plain `AggregateMap` form still accepted. -- `projectComputed(schema, build, mode?)` accepts a builder callback whose `{ relation }` argument is bound to the source row inferred from the pipe: `make().pipe(projectComputed(out, ({ relation }) => ({ total: relation("items").sum("qty") })))`. Plain `ComputedProjectionMap` form still accepted. -- `agg()` factory remains as an escape hatch when the builder is built outside a pipe. diff --git a/.changeset/unify-emailer-spans.md b/.changeset/unify-emailer-spans.md deleted file mode 100644 index f9cefdde0..000000000 --- a/.changeset/unify-emailer-spans.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Unify Emailer `sendMail` spans across implementations. `Fake.sendMail` and `Sendgrid.sendMail` now both emit as `Emailer.sendMail` with OTel-standard `messaging.system` attribute (`"fake"` / `"sendgrid"`). diff --git a/.changeset/unify-parsemany-spans.md b/.changeset/unify-parsemany-spans.md deleted file mode 100644 index e72ba4a48..000000000 --- a/.changeset/unify-parsemany-spans.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -Unify `parseMany` span variants under a single span name. The four prior spans (`parseMany`, `parseMany2`, `parseManyProject`, `parseManyCollect`) now all emit as `parseMany` with `app.entity` and `app.query.mode` attributes (`"transform" | "project" | "collect"`). Reuses the same attribute key already set on the parent `Repository.query` span for consistency. `parseManyProject`/`parseManyCollect` previously lacked `app.entity` — now included. diff --git a/.changeset/update-effect-4.0.0.md b/.changeset/update-effect-4.0.0.md deleted file mode 100644 index a86aab1a6..000000000 --- a/.changeset/update-effect-4.0.0.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/cli": patch ---- - -Upgrade Effect packages to npm `4.0.0`. diff --git a/.changeset/update-effect-4.0.1.md b/.changeset/update-effect-4.0.1.md deleted file mode 100644 index 97a17687b..000000000 --- a/.changeset/update-effect-4.0.1.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/cli": patch ---- - -Upgrade Effect packages to npm `4.0.1`. diff --git a/.changeset/update-effect-beta-107.md b/.changeset/update-effect-beta-107.md deleted file mode 100644 index 37966a213..000000000 --- a/.changeset/update-effect-beta-107.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - -API adaptations for beta.107: - -- `concurrency: "inherit"` → `"unbounded"` -- `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` -- `Schema.LazyArbitrary` → `Schema.Arbitrary` -- `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) -- `SchemaIssue` constructors no longer take `Option` (annotations + input) -- filter meta via `annotations.representation` instead of `annotations.meta` -- `context.defaultValue` → `context.constructorDefault` (single Link) -- Class detection via `~constructor` + static `identifier` -- Redacted detection via `representation.id` -- localized StandardSchema hooks updated for new issue/input model -- provide `NodeCrypto.layer` for cluster sqlite tests -- default `sync-effect` subtree URL → `Effect-TS/effect` diff --git a/.changeset/update-effect-beta-36.md b/.changeset/update-effect-beta-36.md deleted file mode 100644 index 56d7b4fed..000000000 --- a/.changeset/update-effect-beta-36.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined diff --git a/.changeset/update-effect-beta-52.md b/.changeset/update-effect-beta-52.md deleted file mode 100644 index c92200422..000000000 --- a/.changeset/update-effect-beta-52.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch -"@effect-app/cli": patch ---- - -Update effect packages to 4.0.0-beta.52 diff --git a/.changeset/update-effect-beta-60-rpc-custom.md b/.changeset/update-effect-beta-60-rpc-custom.md deleted file mode 100644 index f82562444..000000000 --- a/.changeset/update-effect-beta-60-rpc-custom.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch ---- - -Update to effect 4.0.0-beta.60 and use native `Rpc.custom` constructors (`makeCommandRpc`, `makeStreamRpc`) for metadata-wrapped RPC schemas instead of manually wrapping/unwrapping schemas inline. diff --git a/.changeset/update-effect-beta-66.md b/.changeset/update-effect-beta-66.md deleted file mode 100644 index de3c07470..000000000 --- a/.changeset/update-effect-beta-66.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Update to effect 4.0.0-beta.66. Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). diff --git a/.changeset/update-effect-beta-67.md b/.changeset/update-effect-beta-67.md deleted file mode 100644 index 3bf547c7d..000000000 --- a/.changeset/update-effect-beta-67.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Update to effect 4.0.0-beta.67. Switch deps from `pkg.pr.new` snapshot back to npm beta tag. diff --git a/.changeset/update-effect-beta-83.md b/.changeset/update-effect-beta-83.md deleted file mode 100644 index 0c4776c63..000000000 --- a/.changeset/update-effect-beta-83.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch -"@effect-app/vue-components": patch ---- - -Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - -Adapt the infra workflow engines to beta.83 API changes: - -- `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). -- `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). diff --git a/.changeset/update-effect-beta-84.md b/.changeset/update-effect-beta-84.md deleted file mode 100644 index 5f2113b94..000000000 --- a/.changeset/update-effect-beta-84.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). diff --git a/.changeset/update-effect-beta-86.md b/.changeset/update-effect-beta-86.md deleted file mode 100644 index 49e0b8a74..000000000 --- a/.changeset/update-effect-beta-86.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. diff --git a/.changeset/update-effect-beta-88.md b/.changeset/update-effect-beta-88.md deleted file mode 100644 index 91af448b3..000000000 --- a/.changeset/update-effect-beta-88.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. diff --git a/.changeset/update-effect-beta-90.md b/.changeset/update-effect-beta-90.md deleted file mode 100644 index b8ad24f7c..000000000 --- a/.changeset/update-effect-beta-90.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. diff --git a/.changeset/update-effect-pkgprnew.md b/.changeset/update-effect-pkgprnew.md deleted file mode 100644 index cfdcd8fcb..000000000 --- a/.changeset/update-effect-pkgprnew.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/vue": patch ---- - -Update to effect `pkg.pr.new` snapshot at `a42ef66` (4.0.0-beta.66). Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). diff --git a/.changeset/update-effect-rc-112.md b/.changeset/update-effect-rc-112.md deleted file mode 100644 index 60c243f63..000000000 --- a/.changeset/update-effect-rc-112.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"@effect-app/vue-components": patch -"effect-app": patch -"@effect-app/infra": patch -"@effect-app/cli": patch -"@effect-app/vue": patch ---- - -Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - -API adaptations for rc.112: - -- cluster encoded driver `resetAddress` → batched `resetAddresses` -- Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) -- Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization -- `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) -- JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) diff --git a/.changeset/update-effect-rc-115.md b/.changeset/update-effect-rc-115.md deleted file mode 100644 index 2dd860a21..000000000 --- a/.changeset/update-effect-rc-115.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/cli": patch ---- - -Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. diff --git a/.changeset/upset-aliens-bathe.md b/.changeset/upset-aliens-bathe.md deleted file mode 100644 index 095b7cf59..000000000 --- a/.changeset/upset-aliens-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/infra": patch ---- - -replace useless span diff --git a/.changeset/upset-bugs-roll.md b/.changeset/upset-bugs-roll.md deleted file mode 100644 index b2ee1327e..000000000 --- a/.changeset/upset-bugs-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": patch ---- - -set default barrel ext to .ts diff --git a/.changeset/version-independent-snapshot.md b/.changeset/version-independent-snapshot.md deleted file mode 100644 index 439a300d9..000000000 --- a/.changeset/version-independent-snapshot.md +++ /dev/null @@ -1,5 +0,0 @@ ---- - ---- - -Run Changesets version in parallel with pkg.pr.new snapshot publish on main. diff --git a/.changeset/vue-components-src-exports.md b/.changeset/vue-components-src-exports.md deleted file mode 100644 index c4ef29f70..000000000 --- a/.changeset/vue-components-src-exports.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Point the package `exports` at `src` (`.vue`/`.ts`) for workspace/source consumption, matching `effect-app` and `@effect-app/vue`. The published package is unchanged — `publishConfig.exports` still ships the built `dist` artifacts, so registry consumers keep getting compiled output. This lets linked/source consumers (e.g. via embedded-source mode) load the components from `src` without a build step; runtime `.vue` is compiled by the consumer's bundler and types resolve via `vue-tsc`. diff --git a/.changeset/vue-components-vuetify-typecheck.md b/.changeset/vue-components-vuetify-typecheck.md deleted file mode 100644 index fd35ccff1..000000000 --- a/.changeset/vue-components-vuetify-typecheck.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -OmegaForm/Vuetify: fix `@update:model-value` handler type mismatches on `v-text-field`, `v-textarea` and `v-radio-group` (wrap `field.handleChange` so Vuetify's concrete event value is accepted), and type the `v-radio` `:key`. Drop the stale `@vue-skip` on `Dialog` slot pass-through. - -Tooling: bind Vuetify component types during type-checking via a typecheck-only `types/vuetify-shims.d.ts` so `` etc. resolve in the editor and `pnpm check`, with declaration emit isolated in `tsconfig.build.json` (no Vuetify global augmentation leaks into published types, no runtime bundle). Enable `noUnusedLocals`/`noUnusedParameters` in the check config. diff --git a/.changeset/vue-placeholder-data-fallback.md b/.changeset/vue-placeholder-data-fallback.md deleted file mode 100644 index 629fabaa4..000000000 --- a/.changeset/vue-placeholder-data-fallback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Restore TanStack `placeholderData` / `initialData` as a display-level fallback in query views. While pending with no cached value, `data` now returns the resolved placeholder (with `select` applied when set); neither option is written to the atom cache. diff --git a/.changeset/vue-prefer-fn-untraced.md b/.changeset/vue-prefer-fn-untraced.md deleted file mode 100644 index e303635eb..000000000 --- a/.changeset/vue-prefer-fn-untraced.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Replace `(...) => Effect.gen` with `Effect.fnUntraced` in commander `withDefaultToast`. diff --git a/.changeset/vue-stream-query.md b/.changeset/vue-stream-query.md deleted file mode 100644 index 6e06cc5a6..000000000 --- a/.changeset/vue-stream-query.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add `streamQuery` support for stream-type Rpc handlers. When an Rpc is of type `"stream"`, the client now exposes a `.streamQuery` property (and `...StreamQuery` in helpers) that uses `streamedQuery` from `@tanstack/query-core` to accumulate chunks reactively as an `AsyncResult`. diff --git a/.changeset/vue-suspense-result-race.md b/.changeset/vue-suspense-result-race.md deleted file mode 100644 index 836d8db41..000000000 --- a/.changeset/vue-suspense-result-race.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Wait for Vue query result refs to settle after TanStack suspense resolves. diff --git a/.changeset/vue-unified-invalidation-foreach.md b/.changeset/vue-unified-invalidation-foreach.md deleted file mode 100644 index ed1c31980..000000000 --- a/.changeset/vue-unified-invalidation-foreach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Merge client and server-driven cache invalidation keys into a single `Effect.forEach` pass so server keys always run regardless of whether client targets are empty or custom invalidation options are used. diff --git a/.changeset/wacky-deer-decide.md b/.changeset/wacky-deer-decide.md deleted file mode 100644 index 8591c7342..000000000 --- a/.changeset/wacky-deer-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -fix forms diff --git a/.changeset/warm-maps-reflect.md b/.changeset/warm-maps-reflect.md deleted file mode 100644 index 15e923726..000000000 --- a/.changeset/warm-maps-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -Run projected query decoders through Effect so schemas that require services can resolve during legacy TanStack query refetches. diff --git a/.changeset/warm-streets-smell.md b/.changeset/warm-streets-smell.md deleted file mode 100644 index 7629b1723..000000000 --- a/.changeset/warm-streets-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue-components": patch ---- - -Bump diff --git a/.changeset/wet-roses-press.md b/.changeset/wet-roses-press.md deleted file mode 100644 index 982956ec0..000000000 --- a/.changeset/wet-roses-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix - class should check type side diff --git a/.changeset/wet-sites-fall.md b/.changeset/wet-sites-fall.md deleted file mode 100644 index 6f61aa69a..000000000 --- a/.changeset/wet-sites-fall.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect-app/vue-components": major -"effect-app": major -"@effect-app/infra": major -"@effect-app/vue": major ---- - -Fix Schema->Codec diff --git a/.changeset/wild-olives-bathe.md b/.changeset/wild-olives-bathe.md deleted file mode 100644 index 193bb88d8..000000000 --- a/.changeset/wild-olives-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix ForceVOid diff --git a/.changeset/wild-wombats-nail.md b/.changeset/wild-wombats-nail.md deleted file mode 100644 index 1c38218fe..000000000 --- a/.changeset/wild-wombats-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -add deprecation notices diff --git a/.changeset/wise-items-warn.md b/.changeset/wise-items-warn.md deleted file mode 100644 index 934f3a299..000000000 --- a/.changeset/wise-items-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect-app": patch ---- - -fix ForceVoid for real diff --git a/.changeset/wise-stamps-crash.md b/.changeset/wise-stamps-crash.md deleted file mode 100644 index e7ceb8cb1..000000000 --- a/.changeset/wise-stamps-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -update tanstack/query diff --git a/.changeset/withdefaulttoaststream-progress.md b/.changeset/withdefaulttoaststream-progress.md deleted file mode 100644 index 5210c8ea5..000000000 --- a/.changeset/withdefaulttoaststream-progress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect-app/vue": patch ---- - -`Command.withDefaultToastStream`: add `progress` option to update the waiting toast with progress text on each stream element diff --git a/.changeset/witty-bats-return.md b/.changeset/witty-bats-return.md deleted file mode 100644 index a69a12acf..000000000 --- a/.changeset/witty-bats-return.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect-app/eslint-codegen-model": major -"effect-app": major -"@effect-app/infra": major -"@effect-app/vue": major -"@effect-app/vue-components": major ---- - -Effect v4 beta diff --git a/.changeset/wrap-stream-command.md b/.changeset/wrap-stream-command.md deleted file mode 100644 index 9c76433de..000000000 --- a/.changeset/wrap-stream-command.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -"@effect-app/vue": minor ---- - -Add `wrapStream` support to `Command` with separate `result` and `running` props. - -**Key design:** - -- `result` is always the command's own execution outcome (from `asResult`) -- `running` holds the stream's live `AsyncResult` ref for progress tracking - -**New behaviour:** - -- `CommanderImpl.wrapStream(mutation)` returns a callable like `wrap` — `wrapStream(mutation)()` gives `CommandOut`. -- Accepts either `{ id, mutateStream: [...] }` or the augmented tuple directly (when `.id` is attached). -- `Command.wrap` now accepts `{ mutateStream, id }` and the augmented tuple — both delegate to `wrapStream`. -- `FnOptions.progress` — pass a `ComputedRef` to any `fn`-created command; surfaces as `running`. -- `StreamMutationWithExtensions` now includes `.id` on the tuple. -- Stream client entries expose `wrapStream` (callable), `fn`, and `mutateStream` (with `.id`). -- Stream mutation helpers also carry `.fn` and `.id`. - -```ts -// Via client entry: -const exportCmd = Command.wrapStream(client.myExport)() -// exportCmd.result = own execution result; exportCmd.running = live stream AsyncResult - -// Via mutateStream tuple (id is attached): -const exportCmd = Command.wrapStream(client.myExport.mutateStream)() - -// wrap also accepts the tuple: -const exportCmd = Command.wrap(client.myExport.mutateStream)() - -// fn with external progress: -const cmd = Command.fn({ - id: "myExport", - progress: client.myExport.mutateStream[0] -})( - function*(arg) { - yield* client.myExport.mutateStream[1](arg) - } -) -// cmd.running === the stream AsyncResult ref -``` diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 824fd22d2..000000000 --- a/.claude/settings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(git commit -m ':*)", - "Bash(pnpm -r check)", - "Bash(pnpm lint-fix:*)", - "Bash(git commit -m ' *)", - "Read(//home/patroza/pj/effect-app/**)", - "Bash(git checkout *)", - "Bash(pnpm --filter @effect-app/vue check)", - "Bash(git -C /home/patroza/pj/effect-app/libs status)", - "Bash(git -C /home/patroza/pj/effect-app/libs diff --stat)", - "Bash(git -C /home/patroza/pj/effect-app/libs log -5 --oneline)", - "Bash(git -C /home/patroza/pj/effect-app/libs add packages/infra/src/Model/Repository/ext.ts packages/infra/src/Model/Repository/internal/internal.ts packages/infra/src/Model/Repository/service.ts .changeset/repo-mutations-accept-empty-arrays.md)", - "Bash(git -C /home/patroza/pj/effect-app/libs commit -m ' *)" - ] - } -} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 6786e9b55..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -# Specifies which operating system image to use. -FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu - -WORKDIR /home/vscode - -# curl and ca-certificates are needed for volta installation -RUN apt-get update \ - && apt-get install -y \ - curl \ - ca-certificates \ - --no-install-recommends - -# Changes user to vscode and the SHELL to bash -USER vscode -SHELL ["/bin/bash", "-c"] - -# since we're starting non-interactive shell, we wil need to tell bash to load .bashrc manually -ENV BASH_ENV ~/.bashrc -# needed by volta() function -ENV VOLTA_HOME /home/vscode/.volta -# make sure packages managed by volta will be in PATH -ENV PATH $VOLTA_HOME/bin:$PATH - -# download volta -RUN curl https://get.volta.sh | bash -# change the working directory to the one where the project lives -WORKDIR /workspaces/macs-configurator - -# and install node and pnpm -RUN volta install node@24 -RUN npm i -g pnpm - -RUN echo - -ENV SHELL=/bin/bash - -RUN echo - -# prevent `npm or yarn` usage in the shell now that pnpm is installed -WORKDIR /home/vscode -COPY usepnpm.sh /tmp/ -RUN cat /tmp/usepnpm.sh >> ~/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 6a267802a..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "Effect App Libs Codespace", - "build": { - "dockerfile": "Dockerfile" - }, - // see https://containers.dev/implementors/json_reference/#remoteUser - // you can change the user name to whatever you want, this is used to avoid installing stuff in the image as root - "remoteUser": "vscode", - "postCreateCommand": "pnpm i && pnpm build", - "hostRequirements": { - "cpus": 4, - "memory": "8gb" - } -} diff --git a/.devcontainer/humanlog.sh b/.devcontainer/humanlog.sh deleted file mode 100644 index d792a7551..000000000 --- a/.devcontainer/humanlog.sh +++ /dev/null @@ -1,2 +0,0 @@ - export HUMANLOG_INSTALL="/home/vscode/.humanlog" - export PATH="$HUMANLOG_INSTALL/bin:$PATH" \ No newline at end of file diff --git a/.devcontainer/usepnpm.sh b/.devcontainer/usepnpm.sh deleted file mode 100644 index 2c34d4098..000000000 --- a/.devcontainer/usepnpm.sh +++ /dev/null @@ -1,26 +0,0 @@ -NPM_PATH=$(which npm) -npm () { - if [ -e pnpm-lock.yaml ] - then - echo "Please use PNPM with this project" - elif [ -e yarn.lock ] - then - echo "Please use Yarn with this project" - else - $NPM_PATH "$@" - fi -} - - -YARN_PATH=$(which yarn) -yarn () { - if [ -e pnpm-lock.yaml ] - then - echo "Please use PNPM with this project" - elif [ -e package-lock.json ] - then - echo "Please use NPM with this project" - else - $YARN_PATH "$@" - fi -} \ No newline at end of file diff --git a/.envrc b/.envrc index 03113d3db..3550a30f2 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1 @@ use flake - -# Repo `gh` policy shim: publishing runs the ship gate first (see AGENTS.md). -# Installed by scripts/install-git-hooks.mjs; run `direnv allow` once after cloning. -PATH_add .tools/bin diff --git a/.githooks/README.md b/.githooks/README.md deleted file mode 100644 index 147308b75..000000000 --- a/.githooks/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Native Git hooks - -`pnpm install` configures `core.hooksPath=.githooks` via -`scripts/install-git-hooks.mjs`. Tracked hooks exist in every linked worktree. - -## `post-checkout` - -### 1. Clear TypeScript project emit (always on branch / worktree checkout) - -`pnpm check` is `tsgo --build` with composite + incremental. Package outputs -(`.tsbuildinfo` + `.d.ts` / `.js`) live under `packages/*/dist` and are -gitignored. - -After a branch switch, **sources** follow HEAD but **dist** can still belong to -another tree. tsgo may treat projects as up to date and typecheck new sources -against old declarations → phantom type errors that disappear on -`pnpm rbuild` (`clean` + check). Same class of failure as -[scanner#1495](https://github.com/macs-holding/scanner/pull/1495) / -[scanner#2166](https://github.com/macs-holding/scanner/pull/2166). - -On every **branch** checkout (`post-checkout` flag `1`), this hook deletes: - -- `packages/*/dist`, `packages/*/build`, root `dist` if present -- any remaining `*.tsbuildinfo` outside `node_modules` / `repos` / `.git` - -File-only checkouts (flag `0`) are left alone. - -**This is a workaround.** Root cause belongs in tsgo incremental invalidation -(e.g. [typescript-go#2666](https://github.com/microsoft/typescript-go/issues/2666), -[#4664](https://github.com/microsoft/typescript-go/issues/4664), -[#4262](https://github.com/microsoft/typescript-go/issues/4262)). Remove when -upstream is reliable. Do not re-enable cross-tree dist reuse without an exact -source-identity key. - -### 2. Reconcile pnpm (when package/lock changed) - -Records package/lockfile state under `node_modules` so T3 handoff does not -repeat an install Git already completed. - -When T3 launches the configured worktree setup script, it sets -`T3CODE_DEFER_DEPENDENCY_INSTALL=1`. Cleanup still runs synchronously, while the -setup terminal installs dependencies asynchronously with hardlink imports from -the shared pnpm store. Raw Git worktrees still install during checkout. Their -install failure is best-effort; T3 can request the real exit status with -`T3CODE_WORKTREE_PREPARATION_STRICT=1` without removing the created worktree. - -## `pre-commit` / `pre-push` - -lint-staged on commit; agent ship gate on push (see repo AGENTS.md). diff --git a/.githooks/post-checkout b/.githooks/post-checkout deleted file mode 100755 index e938e7375..000000000 --- a/.githooks/post-checkout +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh -# 1) After a branch/worktree checkout: drop TypeScript project-reference emit so -# tsgo --build never typechecks against foreign-tree .tsbuildinfo / .d.ts. -# 2) Reconcile pnpm when package.json / lockfile changed. - -PATH="$HOME/.local/bin:$HOME/.local/share/pnpm:$HOME/.local/share/mise/shims:/run/current-system/sw/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:$PATH" -export PATH - -# post-checkout also runs after file-only checkouts; those are not a tree switch. -[ "${3:-0}" = "1" ] || exit 0 - -wt_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 -[ -f "$wt_root/package.json" ] || exit 0 - -# --- TS build-cache wipe (every branch / worktree checkout) ----------------- -# -# Why: `pnpm check` is `tsgo --build` with composite + incremental. Package -# outputs live in packages/*/dist (gitignored). After checkout/rebase, sources -# belong to HEAD but dist may still be from another branch. tsgo can treat -# projects as up-to-date and typecheck new sources against old .d.ts → phantom -# errors that vanish on `pnpm rbuild` (clean+check). -# -# Same class as macs-holding/scanner#1495 / #2166 and upstream typescript-go -# incremental gaps (#2666, #4664, #4262, #3787). -# -# Workaround only — remove when upstream invalidation is reliable. Do not -# re-introduce foreign dist reuse without an exact source-identity key. -# -clean_ts_project_emit() { - root=$1 - removed=0 - - for d in \ - "$root"/packages/*/dist \ - "$root"/packages/*/build \ - "$root"/dist - do - [ -e "$d" ] || continue - rm -rf "$d" - removed=1 - done - - if [ -d "$root" ]; then - tsbuild_files=$( - find "$root" \ - \( -path "$root/node_modules" -o -path "$root/repos" -o -path "$root/.git" -o -path "$root/.run" -o -path "$root/build" \) -prune -o \ - -type f -name '*.tsbuildinfo' -print 2>/dev/null || true - ) - if [ -n "$tsbuild_files" ]; then - printf '%s\n' "$tsbuild_files" | while IFS= read -r f; do - [ -n "$f" ] || continue - rm -f "$f" - done - removed=1 - fi - fi - - if [ "$removed" = 1 ]; then - echo "post-checkout: cleared TypeScript project emit (dist / tsbuildinfo) under $root" >&2 - echo "post-checkout: workaround for tsgo stale incremental cache after tree switch — see .githooks/README.md (remove when upstream fixed)" >&2 - fi -} - -clean_ts_project_emit "$wt_root" - -# T3 launches the configured worktree setup command in a visible terminal. Keep -# cleanup synchronous, but leave dependency installation to that async setup. -if [ "${T3CODE_DEFER_DEPENDENCY_INSTALL:-0}" = "1" ]; then - echo "post-checkout: dependency installation deferred to T3 workspace initialization" >&2 - exit 0 -fi - -# --- pnpm install when lockfile / package.json changed ----------------------- -[ -f "$wt_root/pnpm-lock.yaml" ] || exit 0 -state_file="$wt_root/node_modules/.pnpm-checkout-state" -state=$(cksum "$wt_root/package.json" "$wt_root/pnpm-lock.yaml" | cksum | awk '{print $1 ":" $2}') -if [ -f "$wt_root/node_modules/.modules.yaml" ] && [ "$(cat "$state_file" 2>/dev/null)" = "$state" ]; then - exit 0 -fi -command -v pnpm >/dev/null 2>&1 || { - echo "post-checkout: pnpm not on PATH; node_modules not installed" >&2 - exit 1 -} - -export CI=1 -echo "post-checkout: pnpm install in $wt_root" >&2 -cd "$wt_root" || exit 1 -if pnpm install --frozen-lockfile --prefer-offline \ - --config.package-import-method=hardlink \ - --config.confirmModulesPurge=false -then - printf '%s\n' "$state" >"$state_file" -else - status=$? - echo "post-checkout: dependency preparation failed (exit $status); worktree remains usable but degraded" >&2 - [ "${T3CODE_WORKTREE_PREPARATION_STRICT:-0}" = "1" ] && exit "$status" - exit 0 -fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index 8fd0ddf8c..000000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 1 -if [ ! -x "$root/node_modules/.bin/lint-staged" ]; then - echo "pre-commit: worktree preparation is incomplete (lint-staged is missing)" >&2 - echo "pre-commit: run pnpm install before committing" >&2 - exit 1 -fi -# The binary directly, not `pnpm lint-staged`: pnpm runs a dependency-status -# check first and will try to purge node_modules when the lockfile moved, which -# aborts without a TTY — the commit then fails with a pnpm error that says -# nothing about linting. The guard above already established this path exists. -exec "$root/node_modules/.bin/lint-staged" diff --git a/.githooks/pre-push b/.githooks/pre-push deleted file mode 100755 index 6ba4a4cba..000000000 --- a/.githooks/pre-push +++ /dev/null @@ -1,17 +0,0 @@ -# Agent-only ship gate (humans: no-op). See scripts/agent-pre-push.mjs. -# Every push (draft included): check + lint + test. No browser/API suite exists -# here, so there is nothing a draft could usefully defer. -# Publish: pnpm pr:ready, or raw `gh pr ready` — the shim runs the same gate first. -# -# Humans: SKIP_AGENT_PREPUSH=1 git push -# Agents: never SKIP_AGENT_PREPUSH / never --no-verify -# Nested Git commands must discover their own repositories, not inherit this -# hook's GIT_DIR/GIT_WORK_TREE from the outer push. -for name in $(git rev-parse --local-env-vars); do - unset "$name" -done -# Plain `node`, not `pnpm exec node`: pnpm runs a dependency-status check first -# and will try to purge node_modules when the lockfile moved (a rebase does it), -# which aborts without a TTY — the hook then fails with a pnpm error instead of -# anything about the gate. The gate puts node_modules/.bin on PATH itself. -node scripts/agent-pre-push.mjs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 65e15abd8..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -github: [patroza] diff --git a/repos/effect/.github/actions/deploy-website/action.yml b/.github/actions/deploy-website/action.yml similarity index 100% rename from repos/effect/.github/actions/deploy-website/action.yml rename to .github/actions/deploy-website/action.yml diff --git a/repos/effect/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml similarity index 100% rename from repos/effect/.github/actions/setup/action.yaml rename to .github/actions/setup/action.yaml diff --git a/.github/npm-trusted-publishing.md b/.github/npm-trusted-publishing.md deleted file mode 100644 index 1d8ef5874..000000000 --- a/.github/npm-trusted-publishing.md +++ /dev/null @@ -1,85 +0,0 @@ -# npm Trusted Publishing (OIDC) - -Publish from GitHub Actions without a long-lived `NPM_TOKEN`. -npm mints a short-lived credential per workflow run via OIDC. - -Official docs: https://docs.npmjs.com/trusted-publishers/ - -## Prerequisites - -- You must be an **owner** (or have settings access) on each package below. -- Workflow that publishes: `.github/workflows/release.yaml` (filename is `release.yaml`). -- GitHub repo: `effect-app/libs` (public, GitHub-hosted runners). - -## Packages to configure - -Configure **each** publishable package (one trusted publisher per package): - -| Package | npm settings URL | -| --- | --- | -| `effect-app` | https://www.npmjs.com/package/effect-app/access | -| `@effect-app/infra` | https://www.npmjs.com/package/@effect-app/infra/access | -| `@effect-app/vue` | https://www.npmjs.com/package/@effect-app/vue/access | -| `@effect-app/vue-components` | https://www.npmjs.com/package/@effect-app/vue-components/access | -| `@effect-app/cli` | https://www.npmjs.com/package/@effect-app/cli/access | -| `@effect-app/eslint-codegen-model` | https://www.npmjs.com/package/@effect-app/eslint-codegen-model/access | -| `@effect-app/eslint-shared-config` | https://www.npmjs.com/package/@effect-app/eslint-shared-config/access | - -If the UI shows **Settings** / **Trusted Publisher** instead of `/access`, use that page for the same package. - -## Exact steps on npmjs.com (per package) - -Repeat for every package in the table: - -1. Open the package page → **Settings** (or the access URL above). -2. Find **Trusted Publisher**. -3. Under **Select your publisher**, choose **GitHub Actions**. -4. Fill in exactly: - - **Organization or user:** `effect-app` - - **Repository:** `libs` - - **Workflow filename:** `release.yaml` - (filename only, including `.yaml` — not `.yml`, not a path) - - **Environment name:** leave **empty** (unless you later add a GitHub Environment to the job) - - **Allowed actions:** select **`npm publish`** (required) -5. Save. - -npm does **not** validate the config on save — mismatches only show up at publish time (often as `E404`). - -## Order of operations (important) - -1. **Configure trusted publishers** for all packages above (npm side). -2. **Merge** the PR that enables OIDC in `.github/workflows/release.yaml` (`id-token: write`, no `NPM_TOKEN` on publish). -3. On push to `main`, the Changesets workflow publishes any unpublished versions. -4. Confirm success in the Actions log: look for - `No NPM_TOKEN found, but OIDC is available - using npm trusted publishing`. -5. After a successful OIDC publish, optionally harden each package: - - **Publishing access** → **Require two-factor authentication and disallow tokens** - - Revoke the old GitHub secret `NPM_TOKEN` and any automation tokens on npm. - -Do **not** set `NPM_TOKEN` on the publish step once OIDC is intended — `changesets/action` will use the long-lived token instead of OIDC when that env var is present. - -## GitHub workflow - -Live workflow: **`.github/workflows/release.yaml`**. - -It includes: - -- `permissions.id-token: write` -- Node `24` (latest 24.x; npm 12 needs Node ≥ `24.15` — pin the major, not a stale patch like `24.14`) -- Workflow upgrades to `npm@latest` after setup-node (OIDC needs npm ≥ `11.5.1`) -- **No** `NPM_TOKEN` on the publish step -- Package `repository.url` points at `https://github.com/effect-app/libs.git` (set in package.json) - -## Troubleshooting - -| Symptom | Likely cause | -| --- | --- | -| `E404` / “could not be found or you do not have permission” | Trusted publisher mismatch (org/repo/workflow filename), or OIDC not used because `NPM_TOKEN` is still set | -| `ENEEDAUTH` / unable to authenticate | Missing `id-token: write`, old npm CLI, or self-hosted runner (not supported) | -| Only some packages publish | Trusted publisher missing on those packages | -| `EBADENGINE` installing `npm@latest` | Node pin too old (e.g. `24.14`); use major `24` so runners are ≥ `24.15` | - -## Optional hardening - -- GitHub **Environment** (e.g. `npm`) on the `version` job with required reviewers; if used, put the same environment name in every package’s trusted publisher config. -- Keep a **read-only** granular npm token only if install needs private packages (not required for this public monorepo today). diff --git a/repos/effect/.github/workflows/ai-codegen.yml b/.github/workflows/ai-codegen.yml similarity index 100% rename from repos/effect/.github/workflows/ai-codegen.yml rename to .github/workflows/ai-codegen.yml diff --git a/repos/effect/.github/workflows/bundle-comment.yml b/.github/workflows/bundle-comment.yml similarity index 100% rename from repos/effect/.github/workflows/bundle-comment.yml rename to .github/workflows/bundle-comment.yml diff --git a/repos/effect/.github/workflows/check.yml b/.github/workflows/check.yml similarity index 98% rename from repos/effect/.github/workflows/check.yml rename to .github/workflows/check.yml index 5bbcfe849..b52e80d53 100644 --- a/repos/effect/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -25,6 +25,8 @@ jobs: uses: ./.github/actions/setup - name: Lint run: pnpm lint + - name: Check JSDoc + run: pnpm jsdocs --check - name: Check circular dependencies run: pnpm circular - name: Generate AI documentation diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6161e56f7..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: CI - -on: - # push: - # branches: [main] # CI on main branch is handled by release.yaml workflow - pull_request: - branches: [main] - workflow_call: - inputs: - publish_snapshot: - description: Run snapshot publish inside this reusable workflow - type: boolean - default: false - workflow_dispatch: - -env: - CI: true - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - pull-requests: write - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run lint - run: pnpm lint - - build: - name: Build - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run build - run: pnpm build - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-output - path: packages/*/dist - retention-days: 1 - if-no-files-found: error - - test: - name: Test - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run tests - run: pnpm test - - # On main, Changesets calls this workflow and then runs `version` with - # `needs: [ci]`. Keep snapshot out of that reusable path so version does - # not wait for pkg.pr.new. Main snapshot runs as a sibling in release.yaml. - snapshot: - name: Publish Snapshot - needs: [build] - if: | - inputs.publish_snapshot || - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || - github.event_name == 'workflow_dispatch' - uses: ./.github/workflows/publish-snapshot.yml - permissions: - contents: read - pull-requests: write - secrets: inherit diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index eb9719ecc..000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # Optional: Add claude_args to customize behavior and configuration - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - # claude_args: '--allowed-tools Bash(gh pr *)' diff --git a/repos/effect/.github/workflows/cluster.yml b/.github/workflows/cluster.yml similarity index 100% rename from repos/effect/.github/workflows/cluster.yml rename to .github/workflows/cluster.yml diff --git a/.github/workflows/effect-bot.yml b/.github/workflows/effect-bot.yml new file mode 100644 index 000000000..3206fac40 --- /dev/null +++ b/.github/workflows/effect-bot.yml @@ -0,0 +1,39 @@ +name: Effect bot + +on: + issue_comment: + types: [created] + +permissions: {} + +jobs: + relay: + if: >- + github.repository == 'Effect-TS/effect' && + startsWith(github.event.comment.body, '/effect-bot') + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + issues: write + pull-requests: write + steps: + # Never check out the PR head/merge ref. Execute only default-branch code. + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + repository: Effect-TS/effect + ref: ${{ github.event.repository.default_branch }} + persist-credentials: false + sparse-checkout: scripts/effect-bot.mjs + sparse-checkout-cone-mode: false + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: 24 + # No dependency install, local actions, build, or PR-controlled code. + # GITHUB_EVENT_PATH is read as JSON by the trusted helper. + - name: Relay maintainer command + run: node scripts/effect-bot.mjs + env: + GITHUB_TOKEN: ${{ github.token }} + MULTICA_EFFECT_BOT_WEBHOOK_URL: ${{ secrets.MULTICA_EFFECT_BOT_WEBHOOK_URL }} + MULTICA_EFFECT_BOT_WEBHOOK_SECRET: ${{ secrets.MULTICA_EFFECT_BOT_WEBHOOK_SECRET }} diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml deleted file mode 100644 index 33852f531..000000000 --- a/.github/workflows/publish-snapshot.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: Publish Snapshot - -on: - workflow_call: - -permissions: - contents: read - pull-requests: write - -env: - CI: true - -jobs: - snapshot: - name: Publish Snapshot - timeout-minutes: 20 - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: build-output - path: packages/ - - - name: Publish snapshots to pkg.pr.new - id: publish - run: pnpm exec pkg-pr-new publish --pnpm ./packages/* - - - name: Write snapshot usage summary - id: usage - shell: bash - run: | - PACKAGE_LOCATORS="${{ steps.publish.outputs.packages }}" - PACKAGE_URLS="${{ steps.publish.outputs.urls }}" - PUBLISHED_SHA="${{ steps.publish.outputs.sha }}" - if [ -z "$PACKAGE_LOCATORS" ] || [ -z "$PACKAGE_URLS" ] || [ -z "$PUBLISHED_SHA" ]; then - echo "pkg-pr-new did not return expected outputs (packages/urls/sha)" >&2 - exit 1 - fi - - { - echo "## Snapshot publish complete" - echo - echo "- SHA: \`${PUBLISHED_SHA}\`" - echo - echo "Published via pkg.pr.new." - echo - echo "### Install package locators" - echo "\`\`\`bash" - echo "$PACKAGE_LOCATORS" - echo "\`\`\`" - echo - echo "### Package URLs" - echo "\`\`\`text" - echo "$PACKAGE_URLS" - echo "\`\`\`" - } >> "$GITHUB_STEP_SUMMARY" - - { - echo "package_locators<> "$GITHUB_OUTPUT" diff --git a/.github/workflows/publish-wiki.yml b/.github/workflows/publish-wiki.yml deleted file mode 100644 index c66cc7707..000000000 --- a/.github/workflows/publish-wiki.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Publish wiki -on: - push: - branches: [main] - paths: - - wiki/** - - .github/workflows/publish-wiki.yml -concurrency: - group: publish-wiki - cancel-in-progress: true -permissions: - contents: write -jobs: - publish-wiki: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: Andrew-Chen-Wang/github-wiki-action@v4 diff --git a/repos/effect/.github/workflows/release-queue.yml b/.github/workflows/release-queue.yml similarity index 100% rename from repos/effect/.github/workflows/release-queue.yml rename to .github/workflows/release-queue.yml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 6e38f2e11..000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,94 +0,0 @@ -name: Changesets -on: - push: - branches: - - main -# id-token: write enables npm Trusted Publishing (OIDC). -# Do not pass NPM_TOKEN on the publish step — changesets/action prefers a -# long-lived token when set and will skip OIDC. -# See .github/npm-trusted-publishing.md for npmjs.com setup. -permissions: - contents: write - pull-requests: write - id-token: write -env: - CI: true - PNPM_CACHE_FOLDER: .pnpm-store -jobs: - ci: - uses: ./.github/workflows/ci.yml - - snapshot: - name: Publish Snapshot - needs: [ci] - if: | - github.actor != 'github-actions[bot]' && - !contains(github.event.head_commit.message, 'chore: update versions') - uses: ./.github/workflows/publish-snapshot.yml - permissions: - contents: read - pull-requests: write - secrets: inherit - - version: - needs: [ci] - timeout-minutes: 15 - runs-on: ubuntu-latest - steps: - - name: checkout code repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - # Major pin = latest 24.x (CI uses `latest`). npm@12 needs Node >= 24.15. - node-version: 24 - registry-url: https://registry.npmjs.org - - # Trusted publishing requires npm CLI >= 11.5.1; keep CLI current on this Node line. - - name: Ensure npm supports OIDC trusted publishing - run: npm install -g npm@latest && npm --version - - - uses: pnpm/action-setup@v3 - name: Install pnpm - id: pnpm-install - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: install packages - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: create and publish versions - uses: changesets/action@v1 - with: - version: pnpm ci:version - commit: "chore: update versions" - title: "chore: update versions" - publish: pnpm ci:publish - createGithubReleases: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/repos/effect/.github/workflows/release.yml b/.github/workflows/release.yml similarity index 100% rename from repos/effect/.github/workflows/release.yml rename to .github/workflows/release.yml diff --git a/repos/effect/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml similarity index 100% rename from repos/effect/.github/workflows/snapshot.yml rename to .github/workflows/snapshot.yml diff --git a/.gitignore b/.gitignore index e98229014..26bba5ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,64 +1,41 @@ -.claude/worktrees -.DS_Store -node_modules/ -*.log -**/*/.next/ -packages/**/dist/ -!**/.eslintrc*.js -.build/ -dist/ -**/bin/*.js -**/bin/*.js.map -**/bin/*.d.ts -**/bin/*.d.ts.map -tsconfig.tsbuildinfo -.tsbuildinfo -.ultra.cache.json -.jest-cache -.next -.data/ -.npmrc -.cache/ -.opencode/ -.env.local +# Generated by Direnv .env -# CMake -cmake-build-*/ - - -# File-based project format -*.iws - -# IntelliJ -out/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# End of https://www.toptal.com/developers/gitignore/api/intellij - -.infra/*_rev.yaml +# Dot files and directories +**/.* +!/.agents/ +!/.changeset/ +!/.envrc +!/.github/ +!/.gitignore +!/.oxlintrc.json +!/.vscode/ + +# Generated by TypeScript +dist/ +build/ +lib/ +**/*.tsbuildinfo -# vitest? -*.timestamp-*.mjs +# Generated by Pnpm +node_modules/ -# nix -.direnv +# Generated by scripts +coverage/ +tmp/ +stats.txt +# Generated by MacOS +.DS_Store -storybook-static/ -run.sh +# Scratchpad files +scratchpad/**/* -# Generated publish staging directories -packages/*/.publish/ +# Agent instructions +/AGENTS.md +/packages/**/AGENTS.md +/packages/**/CLAUDE.md +/packages/**/ai-docs/ -# agent gh shim (generated) + local ship-gate state -.tools/ -.run/ +# Agent config +opencode.json diff --git a/.ncurc.json b/.ncurc.json deleted file mode 100644 index be9722c1c..000000000 --- a/.ncurc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "reject": [ - "glob", - "@types/glob", - "jwks-rsa", - "applicationinsights", - "@faker-js/faker", - "redis", - "@types/redis", - "react-hook-form", - "eslint-plugin-codegen", - "@tanstack/*" - ], - "dep": [ - "prod", - "dev", - "optional", - "peer" - ] -} diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ad2a213cd..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -# @effect-app:registry=https://npm.pkg.github.com diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index b6f5ce1e9..000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v22.23 diff --git a/.oxlintrc.json b/.oxlintrc.json index 78db57e54..b75ec470d 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,40 +1,36 @@ { - "$schema": "./node_modules/@effect/tsgo/oxlint-schema.json", - "extends": ["./packages/eslint-shared-config/src/oxlintrc.json"], - "options": { - "typeAware": true - }, - "plugins": ["effecttsgo"], - "rules": { - "effecttsgo/any-unknown-in-error-context": "off", - "effecttsgo/effect-fn-iife": "off", - "effecttsgo/global-error-in-effect-catch": "off", - "effecttsgo/layer-merge-all-with-dependencies": "off", - "effecttsgo/lazy-effect": "off", - "effecttsgo/return-effect-in-gen": "off", - "effecttsgo/run-effect-inside-effect": "off", - "effecttsgo/schema-sync-in-effect": "off", - "effecttsgo/strict-effect-provide": "off", - "effecttsgo/unsafe-effect-type-assertion": "off", - "effecttsgo/unstable-api-usage": "off", - "effecttsgo/floating-effect": "off", - "effecttsgo/multiple-effect-provide": "off", - "effecttsgo/unknown-in-effect-catch": "off", - "effecttsgo/missing-effect-service-dependency": "error", - "effecttsgo/effect-fn-opportunity": "warn", - "effecttsgo/global-error-in-effect-failure": "warn", - "effecttsgo/prefer-schema-over-json": "warn" - }, - // ignorePatterns overwrites (not merges) the base — all standard patterns must be repeated here. + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "extends": ["./packages/tools/oxc/oxlintrc.json"], "ignorePatterns": [ - "**/dist/**", - "**/node_modules/**", - "**/*.d.ts", - "**/*.js", - "**/*.jsx", - "**/vitest.config.ts", - "**/eslint.*.mjs", + "LLMS.md", + "**/dist", + "**/build", + "**/docs", + "**/*.tsbuildinfo", "**/CHANGELOG.md", - "repos/**" - ] + "packages/effect/typeperf/**/*", + "packages/effect/src/StandardSchema.ts", + "!scratchpad/**/*", + "**/.*", + "!.agents", + "!.agents/**/*", + "!.changeset", + "!.changeset/**/*", + "!.envrc", + "!.github", + "!.github/**/*", + "!.gitignore", + "!.oxlintrc.json", + "!.vscode", + "!.vscode/**/*" + ], + "jsPlugins": ["@effect/oxc/oxlint"], + "overrides": [{ + "files": ["**/{test,typetest,examples,ai-docs,benchmark,bundle,scripts,scratchpad}/**"], + "rules": { + "effect/no-bigint-literals": "off", + "eslint/no-console": "off", + "effect/no-import-from-barrel-package": "off" + } + }] } diff --git a/.specs/Update-to-latest-Effect.md b/.specs/Update-to-latest-Effect.md deleted file mode 100644 index 88edfa6d0..000000000 --- a/.specs/Update-to-latest-Effect.md +++ /dev/null @@ -1,23 +0,0 @@ -# Update to latest effect packages - -## Rules - -- only include root `package.json` and packages under the `packages` folders - -## Steps - -1. run `pnpm test` and `pnpm lint-fix` to compare later -2. update package.json files -3. run `pnpm i` -4. commit the dep bump (package.json files + `pnpm-lock.yaml`) — `pnpm subtree:effect` runs `git subtree pull` which **requires a clean working tree** (`fatal: working tree has modifications. Cannot add.` otherwise). Commit before syncing. -5. run `pnpm subtree:effect` to sync the `repos/effect` subtree to the same version (creates its own squash-merge commit), then run `pnpm i` inside it. -6. run test and lint again, compare to from before the update. -7. create a changeset describing the changes -8. prepare commit (changeset + any test/lint fallout) - -If new errors occur, first describe the problem, propose solutions and wait for answers. - -## Notes - -- subtree url: `https://github.com/Effect-TS/effect.git`, prefix `repos/effect`, tag `effect@`. -- v4 currently lives under the npm `rc` dist-tag (`beta` is frozen at `4.0.0-beta.107`; npm `latest` is still v3). Find target with `npm view effect dist-tags` — prefer `rc` if present, else `beta`. diff --git a/.specs/changeset-publishing.md b/.specs/changeset-publishing.md deleted file mode 100644 index 7bd8ac4a6..000000000 --- a/.specs/changeset-publishing.md +++ /dev/null @@ -1,138 +0,0 @@ -# Changesets Publishing - -## Summary - -Publish `effect-app`, `infra`, `vue` and `vue-components` under unified -versions using changesets' `fixed` groups. - -## Background - -All public packages must move in lockstep under a single version number (`4.0.0-beta.0`, `4.0.0-beta.1`, ...) -so consumers can install a coherent set of packages at any beta iteration. -Changesets supports this via **fixed version groups** (all packages in a group share the same -version). - -## Goals - -- All 4 public library packages publish the same version on every release. -- Developers continue using the standard `pnpm changeset` workflow. - -## Non-goals - -- No changes to the build system, CI pipeline scripts, or package contents. -- No independent versioning — all packages move together. -- No canary/nightly publishing strategy. - -## How it works - -### Fixed groups - -The `fixed` field in `.changeset/config.json` takes an array of package-name -arrays. All packages in a group are bumped to the **same version** whenever any -one of them is included in a changeset. By placing all 24 public packages in one -group, a single changeset touching any package triggers a version bump for every -package. - -### Version stability during pre-release - -A common concern: won't a `minor` or `patch` changeset during the beta change -the base version (e.g., `4.0.0-beta.1` → `4.1.0-beta.0`)? No — changesets -prevents this via `pre.json`. - -`pre.json` stores two critical fields: - -- **`initialVersions`**: the version of each package when pre-release mode was - entered (e.g., `3.0.0`). -- **`changesets`**: the IDs of every changeset applied since entering - pre-release mode. - -When `changeset version` runs, it computes the target version relative to -`initialVersions` using the **cumulative** bump of all tracked changesets (both -already-applied ones in `pre.json.changesets` and new pending ones). Because the -initial major changeset is always in the tracked set, the highest bump type is -always `major`, and the base version stays at `4.0.0` regardless of subsequent -patch or minor changesets. Only the beta counter increments. - -Example: - -1. Enter pre-release, `initialVersions` = `3.0.0` -2. Major changeset → `changeset version` → `4.0.0-beta.0` (major from `3.0.0`) -3. Developer adds a patch changeset → `changeset version` → `4.0.0-beta.1` - (major still highest → base unchanged, counter increments) -4. Developer adds a minor changeset → `changeset version` → `4.0.0-beta.2` - (major still highest → base unchanged, counter increments) - -The base semver never drifts during the beta period. - -### Combined behavior - -With both features active, every `changeset version` + `changeset publish` cycle -produces a release where all 24 packages share `4.0.0-beta.`. - -## Setup steps - -### 1. Update `.changeset/config.json` - -Add the `fixed` group and change `access` to `"public"`: - -## Ongoing workflow - -### Adding changesets - -Developers continue using `pnpm changeset` as usual. Because of the `fixed` -group, the bump type (patch/minor/major) of any individual changeset is -effectively irrelevant to the version number — all packages are already on a -major bump track. However, the changeset message still serves as the changelog -entry, so meaningful descriptions are important. - -### Publishing a new version - -```bash -npx changeset version # bumps to 4.0.0-beta.(n+1) -npx changeset publish # publishes all packages under "beta" tag -``` - -The counter auto-increments. No manual version editing is needed. - -### CI integration - -The existing CI publish workflow (typically `changeset version` + -`changeset publish` in a GitHub Action) works without modification. The -pre-release mode and fixed group are configuration-only — no script changes -required. - -## Graduating to stable - -When ready to release `4.0.0`: - -```bash -npx changeset pre exit # removes pre-release mode -npx changeset version # sets all packages to 4.0.0 -npx changeset publish # publishes to "latest" dist-tag -``` - -After exiting, the `fixed` group can remain to keep packages in lockstep for -future `4.x` releases, or be removed if independent versioning is desired. - -## Edge cases - -### Workspace dependency ranges - -`updateInternalDependencies: "patch"` ensures workspace `dependencies` and -`peerDependencies` are updated whenever a dependency's version changes. Because -all packages share the same version, cross-references will always point to the -current beta. Verify that dependency ranges use `workspace:^` (pnpm protocol) so -they resolve correctly during development and are rewritten to concrete ranges on -publish. - -### npm version collision safety - -Changesets' pre-release counter prevents collisions — `4.0.0-beta.0`, -`4.0.0-beta.1`, etc. are distinct versions. npm's immutability guarantees that a -published version can never be overwritten. - -## Files to modify - -| File | Change | -| ------------------------ | --------------------------------------------- | -| `.changeset/config.json` | Add `fixed` array, set `access` to `"public"` | diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6f2a533fc..1a118e9e7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,6 @@ { "recommendations": [ "oxc.oxc-vscode", - "dprint.dprint", - "dbaeumer.vscode-eslint", - "editorconfig.editorconfig", - "Vue.volar" + "dprint.dprint" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3a998c7db..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "node", - "request": "launch", - "name": "Debug TSC", - "program": "${workspaceFolder}/node_modules/tsc.js", - "args": [ - "--build tsconfig.all.json" - ], - "sourceMaps": false, - "protocol": "inspector", - "configurations": [ - { - "name": "Attach by Process ID", - "processId": "${command:PickProcess}", - "request": "attach", - "skipFiles": [ - "/**" - ], - "type": "node" - } - ] -} diff --git a/.vscode/model.code-snippets b/.vscode/model.code-snippets deleted file mode 100644 index 3802d7720..000000000 --- a/.vscode/model.code-snippets +++ /dev/null @@ -1,77 +0,0 @@ -{ - "ModelCodegen": { - "prefix": "genmod", - "body": [ - "// codegen:start {preset: model}" - ] - }, - "ResourceCodegen": { - "prefix": "genrsc", - "body": [ - "// codegen:start {preset: meta, sourcePrefix: src/resources/}" - ] - }, - "ModelComplete": { - "prefix": "moc", - "body": [ - "export class $1 extends S.ExtendedClass<$1, $1.Encoded>()({", - "$2", - "}) {}" - ], - "description": "Defines a Model signature" - }, - "Model": { - "prefix": "mo", - "body": [ - "export class $1 extends S.Class<$1>()({$2}) {}", - "" - ], - "description": "Defines a Model signature" - }, - "UnionOpaque": { - "prefix": "un", - "body": [ - "const $1__ = union({ $2 })", - "const $1_ = enhanceClassUnion(OpaqueSchema<$1, $1.Encoded>()($1__))", - "export type $1 = To & UnionBrand", - "export interface $1Schema extends Identity {}", - "export const $1: $1Schema = $1_", - "export namespace $1 {", - " export type Encoded = Encoded & UnionBrand", - "}" - ] - }, - "Request": { - "prefix": "req", - "body": [ - "export class $1 extends S.Req<$1>()(\"$1\", {", - " $2", - "}, { success: $3 }) {}", - "" - ], - "description": "Defines a Request signature" - }, - "Response": { - "prefix": "res", - "body": [ - "export class Response extends S.Class", - " Effect.gen(function*() {", - " //const userRepo = yield* UserRepo", - "", - " return {", - " $2: $2($3)", - " }", - " })" - ] - } -} \ No newline at end of file diff --git a/.vscode/service.code-snippets b/.vscode/service.code-snippets deleted file mode 100644 index b3314afcb..000000000 --- a/.vscode/service.code-snippets +++ /dev/null @@ -1,14 +0,0 @@ -{ - "ServiceConstructor": { - "prefix": "svc", - "body": [ - "export class $1 extends Effect.Service<$1>()(\"$1\", {", - " dependencies: [$3],", - " effect: Effect.gen(function*() {", - " $2", - " return {}", - " })", - "}) {}", - ] - } -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 362ed5f03..672eea785 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,69 +1,51 @@ { - "files.exclude": { - "**/.git": false - }, - "explorer.sortOrderLexicographicOptions": "upper", - "typescript.preferences.includePackageJsonAutoImports": "on", - "typescript.experimental.expandableHover": true, - "js/ts.hover.maximumLength": 5000, - "typescript.preferences.autoImportFileExcludePatterns": [ - "node_modules/@sentry/node", - "node_modules/vitest/dist", - "node_modules/@azure/cosmos", - "node_modules/effect-app/Schema", - "node_modules/lodash", - "node_modules/@types/lodash" - ], - "typescript.tsdk": "node_modules/typescript/lib", - "eslint.workingDirectories": [ - { - "pattern": "apps/*" - }, - { - "pattern": "packages/*" - } - ], + "dprint.path": "node_modules/.bin/dprint", "editor.formatOnSave": true, + "editor.defaultFormatter": "dprint.dprint", "editor.formatOnSaveMode": "file", "editor.codeActionsOnSave": { - "source.fixAll.oxc": "explicit", - "source.fixAll.eslint": "explicit", - "source.addMissingImports": "explicit" + "source.fixAll.oxc": "explicit" + }, + "editor.quickSuggestions": { + "other": true, + "comments": false, + "strings": false + }, + "editor.acceptSuggestionOnCommitCharacter": true, + "editor.acceptSuggestionOnEnter": "on", + "editor.quickSuggestionsDelay": 10, + "editor.suggestOnTriggerCharacters": true, + "editor.tabCompletion": "off", + "editor.suggest.localityBonus": true, + "editor.suggestSelection": "recentlyUsed", + "editor.wordBasedSuggestions": "matchingDocuments", + "editor.parameterHints.enabled": true, + "files.insertFinalNewline": true, + "[typescript]": { + "editor.defaultFormatter": "dprint.dprint" }, - "editor.defaultFormatter": "dprint.dprint", - "[vue]": { + "[typescriptreact]": { "editor.defaultFormatter": "dprint.dprint" }, "[javascript]": { "editor.defaultFormatter": "dprint.dprint" }, - "[javascriptreact]": { + "[json]": { "editor.defaultFormatter": "dprint.dprint" }, - "[typescript]": { + "[jsonc]": { "editor.defaultFormatter": "dprint.dprint" }, - "[typescriptreact]": { + "[markdown]": { "editor.defaultFormatter": "dprint.dprint" }, - "eslint.validate": [ - "javascript", - "typescript", - "vue" - ], - "eslint.format.enable": false, - "csv-preview.separator": ";", - "cSpell.words": [ - "codegen", - "composables", - "pipeable", - "tsplus" + "deno.enable": true, + "deno.lint": false, + "deno.enablePaths": [ + "./packages/platform/deno" ], - "files.watcherExclude": { - "**/target": true - }, - "chat.tools.terminal.autoApprove": { - "git add": true, - "git commit": true - } + "js/ts.tsdk.path": "./node_modules/typescript/bin", + "js/ts.tsdk.additionalLocations": ["./node_modules/typescript/bin"], + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "js/ts.experimental.useTsgo": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 2d9f2e8a6..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "tasks": [ - { - "label": "build", - "type": "shell", - "command": "pnpm watch", - "options": { - "cwd": "${workspaceRoot}" - }, - "group": { - "kind": "build", - "isDefault": true - }, - "isBackground": true, - "presentation": { - "group": "watch-build" - }, - "problemMatcher": [ - { - "base": "$tsc-watch", - "fileLocation": [ - "relative", - "${workspaceRoot}" - ] - } - ] - }, - { - "label": "Run Storybook OmegaForm", - "type": "shell", - "command": "pnpm storybook", - "options": { - "cwd": "${workspaceRoot}/packages/vue-components" - }, - "problemMatcher": [] - } - ] -} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 0ca05556b..000000000 --- a/AGENTS.md +++ /dev/null @@ -1,217 +0,0 @@ -# Agent Instructions - -This is the Effect App library repository, focusing on functional programming patterns and effect systems in TypeScript, wrapping and extending the Effect library. - -## Development Workflow - -- The git base branch is `main` -- Use `pnpm` as the package manager - -### Pull requests - -Always open a PR for agent work that changes the repo — do not leave finished work only on a local branch. - -- **Draft early**: open a draft PR as soon as there is a meaningful commit (or when starting multi-step work that will land), so review/CI can track progress while the rest of the work continues. Pushes to a draft run the same gate as any other push — the draft is about review status, not about skipping checks. -- **Keep the PR current**: push commits as you go; update the PR body if scope shifts. -- **Ready when done**: publish with `pnpm pr:ready`, or with plain `gh pr ready` — both run the ship gate first and undraft only if it passes. Do not hand-run the checks beforehand and do not leave a finished, validated change as draft. -- **Base**: target `main` unless the work is explicitly stacked on another branch. - -### The gate runs the checks — you do not - -`.githooks/pre-push` is the agent ship gate. It is a **no-op for humans** and -fires only for coding agents (`GROK_AGENT` / `T3_AGENT` / `AI_AGENT` / Claude / -Cursor / Codex env markers). - -It runs on **every** agent push — draft, ready, or no PR at all. There is no -browser or API suite here that a draft could usefully defer, and a pushed commit -that does not compile or whose tests fail is worth nothing to a reviewer whatever -the PR says. - -The gate is `pnpm check` → `pnpm lint` → `pnpm test`, which is what CI runs and -nothing more. This is a library monorepo — no application to stand up, no browser -suite — so the unit run _is_ the gate. It caches the validated HEAD SHA in -`.run/agent-ship-gate.json`, so **one commit is validated once** however many -times you push or publish it. Force a re-run with `AGENT_SHIP_GATE_FORCE=1`. - -**Do not hand-run `pnpm check` / `lint` / `test` as routine verification.** -Validating the same commit repeatedly costs the same each time and proves nothing -the first run did not. While iterating, narrow proof is the right tool — the one -test you are fixing, or a typecheck of the package you touched. Whole-gate runs -belong to the push. - -`.envrc` puts the `gh` shim on `PATH`; run **`direnv allow`** once after cloning -(`command -v gh` should print `.tools/bin/gh`). Never `--no-verify`, and never set -`SKIP_AGENT_PREPUSH` — that is the human escape hatch. - -### Core Principles - -- **Zero Tolerance for Errors**: All automated checks must pass -- **Root causes, not workarounds**: When something fails — a type error, a runtime crash, a failing test, a hanging fiber — diagnose the invariant that was violated and fix it there. Do **not** mask the symptom with a wrapper, a revive helper, a `try/catch`, a `JSON.parse(JSON.stringify(...))`, or any other shape-coercion. Each of these is a tell that you skipped the diagnosis. -- **No `as any` / `as unknown` casts**: A specific case of the rule above. Casts hide the type system telling you the shape is wrong. Understand the actual types and fix the root cause. If a type mismatch exists, find the correct v4 API, update the type signatures, or restructure the code. -- **No hand-rolled deserialization**: Anything stored as JSON and read back must round-trip through a Schema codec (`S.fromJsonString(S.toCodecJson(...))`, `S.encodeEffect` / `S.decodeEffect`). Class prototypes — `Exit`, `Cause`, `Workflow.Result`, `Schema.Class` instances — do not survive `JSON.parse`. The schema layer is the canonical reconstruction path; writing a custom `reviveX` helper is a smell that the codec is missing. -- **Search prior art before writing helpers**: Before introducing a new utility for a cross-boundary concern (serialization, encoding, retries, context propagation, OCC), grep the repo and `repos/effect` for the established pattern. The cluster engine, the existing Cosmos adapter, the SQL stores — they have already solved most of these problems. Copying the established pattern beats inventing a parallel one. -- **Check newer branches before resuming work**: A new session may resume on `main` with no checkout of relevant feature branches. Before extending a topic, run `git branch -a | grep ` and inspect the latest version with `git show :`. Session-local memory of "how X works" may be from an older draft that has since been replaced. -- **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. -- **Look for effect sources inside `repos/effect`** -- **Never import local `repos` files**: Always use the latest online versions of packages instead. -- **Never webfetch from the effect repos**: just use the locally included under `repos` - -### When you hit an error, before writing any fix - -Errors are signals, not nuisances. Run this checklist: - -1. **State the failure precisely.** Quote the exact error or behavior. `"object is not iterable"` is not the same as `"wrong type"`. The message names the violated invariant. -2. **Name the boundary.** Where in the data flow did the value lose the property the consumer needs? At the storage write, the storage read, the network hop, the schema decode, the context provision? -3. **Search for prior art at that boundary.** `grep` the repo + `repos/effect` for how similar values cross the same boundary. If a pattern exists, use it. -4. **Only then write code.** If your fix re-implements something the searched-for pattern already does (revive prototypes, retry on OCC, encode JSON), stop — use the existing pattern instead. - -Anti-patterns that mean you skipped the checklist: - -- `JSON.parse` of a value that contains tagged classes, followed by manual prototype reconstruction. -- `try/catch` around a yield that swallows the error and returns a fabricated value. -- Adding a second `as any` to make a first `as any` typecheck. -- Adding a sleep / retry to "give it time to work" instead of finding the missing wake signal. -- Disabling a hook (`--no-verify`) or a check to make the diff land. - -### Validation - -The ship gate owns validation — see _The gate runs the checks — you do not_. It -runs `pnpm check` → `pnpm lint` → `pnpm test` on every push and on publish, once -per commit. - -`pnpm lint-fix` is the one thing worth running by hand, because it _writes_: it -formats and auto-fixes across packages, and the gate only reports what it would -have fixed. Run it when you are done editing, stage what it changes, then push. - -If type checking fails in a way that makes no sense against the diff, `pnpm clean` -clears the caches — stale incremental state produces phantom errors. - - - - -## 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 `Context.Service` - -Prefer the class syntax when working with `Context.Service`. For example: - -```ts -import { Context } from "effect-app" - -class MyService extends Context.Service number -}>()("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. - - - - - - - -## Per-request `Effect.provide(layer)` must isolate its MemoMap - -`Effect.provide(self, layer)` resolves its `MemoMap` from the ambient fiber -context. On an HTTP server, that MemoMap lives on the server fiber and is -shared by every request that server handles. The first request to build a -stateful layer (anything using `Layer.effect` / `Effect.acquireRelease`) -memoizes the resulting value onto the server fiber; every subsequent request -then receives the _same instance_ — including its `clear()` / dispose -finalizer, which now fires at the wrong time. - -When you call `Effect.provide(layer)` (or `Stream.provide(layer)`) inside a -per-request hot path: - -- Pass `{ local: true }` if the layer is pure / stateless or you genuinely - want it scoped to _this_ effect only, or -- Build it explicitly against the request scope with a fresh `MemoMap` - (`Layer.makeMemoMap` + `Layer.buildWithMemoMap(layer, memoMap, requestScope)`) - — see `provideOnRequestScope` in `packages/infra/src/setupRequest.ts`. - -If neither option is taken and the layer carries state, the state leaks -across requests. Concrete repro lives in -`packages/infra/test/rpc-context-map-streaming.test.ts` (the overlapping -requests case). - -## 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/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 8359239d2..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,3 +0,0 @@ -# CLAUDE.md - -Strictly follow the rules in ./AGENTS.md diff --git a/repos/effect/LICENSE b/LICENSE similarity index 100% rename from repos/effect/LICENSE rename to LICENSE diff --git a/repos/effect/LLMS.md b/LLMS.md similarity index 100% rename from repos/effect/LLMS.md rename to LLMS.md diff --git a/repos/effect/MIGRATION.md b/MIGRATION.md similarity index 94% rename from repos/effect/MIGRATION.md rename to MIGRATION.md index 98b2c635c..4df8d9eaf 100644 --- a/repos/effect/MIGRATION.md +++ b/MIGRATION.md @@ -45,7 +45,10 @@ with `@stability unstable`. `@stability unstable` means an API may receive breaking changes in minor releases. `@stability experimental` means it may receive breaking changes -across patch versions. APIs without a stability tag follow strict semver. +across patch versions. `@stability stable` and APIs without a stability tag +follow strict semver. Every module and directly importable export declares its +stability explicitly. Modules in packages other than `effect` are currently +`@stability unstable`. APIs that expose a third-party dependency are also marked `@stability unstable`, because that dependency's own releases can change them. This covers diff --git a/README.md b/README.md index 96d052283..8df9d3431 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,97 @@ -# Practical use of Effect-TS + -This is an opinionated library for full-stack [Effect-TS](https://github.com/Effect-TS/core). -(See repositories for more info and discord link, articles, youtube videos, etc). +[![pkg.pr.new](https://img.shields.io/badge/pkg.pr.new-Effect--TS%2Feffect-black)](https://pkg.pr.new/~/Effect-TS/effect) -WIP [docs](https://github.com/effect-ts-app/docs) +# Effect -See https://github.com/effect-ts-app/boilerplate for a sample app use. +Effect is a library for building robust, maintainable, type-safe, and production grade applications in TypeScript. It helps you handle the hard problems at scale: typed errors, dependency injection, structured concurrency, scheduling, tracing, and unified schema validation. -## Package boundaries +> **Effect 4.x is a long-term support (LTS) release.** If you are upgrading from Effect 3.x, follow the [migration guide](MIGRATION.md). -- `effect-app`: service contracts and runtime-agnostic base logic. -- `@effect-app/infra`: backend / Node adapters. -- `@effect-app/vue`: Vue / browser adapters. - -Migration targets introduced in this repo include: - -- `@effect-app/infra/Emailer/service` -> `effect-app/Emailer` -- `@effect-app/infra/QueueMaker/service` -> `effect-app/QueueMaker` -- `@effect-app/infra/Store/service` -> `effect-app/Store` -- `@effect-app/infra/Model/*` -> `effect-app/Model/*` -- `@effect-app/vue/runtime` -> `effect-app/runtime` -- `@effect-app/vue/toast` -> `effect-app/toast` -- `@effect-app/vue/withToast` -> `effect-app/withToast` - -## Update Effect Subtree - -`repos/effect` is a git subtree of `https://github.com/Effect-TS/effect-smol.git`. - -Recommended (uses the currently pinned `effect` / `@effect/*` version from package.json): - -```sh -pnpm subtree:effect -``` - -Or directly via the CLI: - -```sh -effa sync-effect -``` - -Use a different repository URL: - -```sh -effa sync-effect --url https://github.com/Effect-TS/effect-smol.git -``` - -Manual update to a specific ref: - -```sh -git subtree pull --prefix=repos/effect https://github.com/Effect-TS/effect-smol.git --squash -``` - -Example: - -```sh -git subtree pull --prefix=repos/effect https://github.com/Effect-TS/effect-smol.git @effect/ai-anthropic@4.0.0-beta.47 --squash -``` - -## Update Effect App Libs Subtree - -Apps that vendor this repository as `repos/libs` can sync it to the pinned -`effect-app` / `@effect-app/*` version in their package.json: - -```sh -effa sync-effect-app -``` - -The command first tries Changesets package tags such as -`effect-app@4.0.0-beta.271` or `@effect-app/infra@4.0.0-beta.271`. If no -matching tag exists, it fetches `https://github.com/effect-app/libs.git` into a -local git cache and selects the latest commit on the default branch whose -package manifest still has the pinned version. - -Escape hatch for manual refs: +## Installation ```sh -effa sync-effect-app --ref main -effa sync-effect-app --ref latest # latest main -``` - -Both `sync-effect` and `sync-effect-app` fetch selected commits with -`--no-tags`, so syncing subtrees does not import upstream package tags into the -app repository. - -The clean release convention is to publish git tags for package versions -(`effect-app@`, `@effect-app/infra@`, etc.) from the release -workflow. The history scan exists because this repo still has older `v*` tags -but not current package-version tags. - -## Deployment - -Uses [Changesets](https://github.com/changesets/changesets/blob/main/README.md) - -1. make changes -2. generate and include changeset `pnpm changeset` -3. wait for build which creates a PR -4. inspect the PR, merge when alright -5. await new build and new package deployments - -## Thanks - -- All contributors -- Michael Arnaldi, Max Brown and the Effect-TS contributors for Effect - - ZIO Contributors for the excellent ZIO -- Anyone else we're forgetting.. - -## How to view OmegaForm docs - -On Mac: - -1. `Shift + Command + P` -2. Select `Run Task` -3. Select `Run Storybook OmegaForm` -4. Will automatically open browser to http://localhost:6006/ Enjoy! - -On Windows: - -1. Select "Run Task" - -```bash -cd packages/vue-components -``` - -2. run storybook - -```bash -pnpm storybook +npm install effect ``` -3. Will automatically open browser to http://localhost:6006/ Enjoy! +## Requirements + +- **TypeScript 5.9 or newer.** TypeScript 7 is recommended for the best performance and compatibility with [Effect's TypeScript tooling](https://github.com/Effect-TS/tsgo#installation). +- **Node.js 18 or newer** is the general minimum for running Effect on Node.js. Some integration packages require newer runtimes; for example, `@effect/sql-sqlite-node` requires Node.js 22.16 or newer. +- **Strict type-checking:** the `strict` flag must be enabled in your `tsconfig.json`. + +## Links + +- [Website](https://effect.website): documentation, guides, and news +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests +- [Jobs](https://effect.website/effect-jobs): companies hiring Effect developers +- Follow us on [X](https://x.com/EffectTS_), [Bluesky](https://bsky.app/profile/effect-ts.bsky.social), and [LinkedIn](https://www.linkedin.com/company/effect-ts) + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. + +Feedback from teams using and evaluating Effect directly shapes what we stabilize and build next. + +## Long-term support + +Teams depend on Effect for systems they expect to run for years. Effect 4.x is a long-term support (LTS) release with the following guarantees: + +- At least three years of support, including bug and security fixes. +- Bug fixes for one year after the next major version is released. +- Security fixes for two years after the next major version is released. + +Stable APIs reserve breaking changes for major releases. APIs marked unstable may change in minor releases, and experimental APIs may change in patch releases. + +## Effect v3 + +The Effect v3 source code is available on the [`v3`](https://github.com/Effect-TS/effect/tree/v3) branch, which is also where issues and pull requests meant for Effect v3 should be targeted. To upgrade, see the [migration guide](MIGRATION.md). + +## Packages + +This monorepo contains the core `effect` package alongside integration packages that extend it. All packages listed below are released together with synchronized versions. + +| Package | Description | API Reference | +| --------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------ | +| [`effect`](packages/effect) | The core package | [docs](https://effect.website/docs/v4/api/effect) | +| [`@effect/platform-browser`](packages/platform/browser) | Platform services for the browser | [docs](https://effect.website/docs/v4/api/platform-browser) | +| [`@effect/platform-bun`](packages/platform/bun) | Platform services for [Bun](https://bun.sh) | [docs](https://effect.website/docs/v4/api/platform-bun) | +| [`@effect/platform-deno`](packages/platform/deno) | Platform services for [Deno](https://deno.com) | [docs](https://effect.website/docs/v4/api/platform-deno) | +| [`@effect/platform-node`](packages/platform/node) | Platform services for [Node.js](https://nodejs.org) | [docs](https://effect.website/docs/v4/api/platform-node) | +| [`@effect/platform-node-shared`](packages/platform/node-shared) | Shared services for Node.js-compatible runtimes | [docs](https://effect.website/docs/v4/api/platform-node-shared) | +| [`@effect/sql-clickhouse`](packages/sql/clickhouse) | SQL client for [ClickHouse](https://clickhouse.com) | [docs](https://effect.website/docs/v4/api/sql-clickhouse) | +| [`@effect/sql-d1`](packages/sql/d1) | SQL client for Cloudflare D1 | [docs](https://effect.website/docs/v4/api/sql-d1) | +| [`@effect/sql-libsql`](packages/sql/libsql) | SQL client for libSQL | [docs](https://effect.website/docs/v4/api/sql-libsql) | +| [`@effect/sql-mssql`](packages/sql/mssql) | SQL client for Microsoft SQL Server | [docs](https://effect.website/docs/v4/api/sql-mssql) | +| [`@effect/sql-mysql2`](packages/sql/mysql2) | SQL client for MySQL | [docs](https://effect.website/docs/v4/api/sql-mysql2) | +| [`@effect/sql-pg`](packages/sql/pg) | SQL client for PostgreSQL | [docs](https://effect.website/docs/v4/api/sql-pg) | +| [`@effect/sql-pglite`](packages/sql/pglite) | SQL client for [PGlite](https://pglite.dev) | [docs](https://effect.website/docs/v4/api/sql-pglite) | +| [`@effect/sql-sqlite-bun`](packages/sql/sqlite-bun) | SQL client for SQLite via `bun:sqlite` | [docs](https://effect.website/docs/v4/api/sql-sqlite-bun) | +| [`@effect/sql-sqlite-do`](packages/sql/sqlite-do) | SQL client for Cloudflare Durable Objects SQLite | [docs](https://effect.website/docs/v4/api/sql-sqlite-do) | +| [`@effect/sql-sqlite-node`](packages/sql/sqlite-node) | SQL client for SQLite via `node:sqlite` | [docs](https://effect.website/docs/v4/api/sql-sqlite-node) | +| [`@effect/sql-sqlite-react-native`](packages/sql/sqlite-react-native) | SQL client for SQLite in React Native | [docs](https://effect.website/docs/v4/api/sql-sqlite-react-native) | +| [`@effect/sql-sqlite-wasm`](packages/sql/sqlite-wasm) | SQL client for SQLite compiled to WebAssembly | [docs](https://effect.website/docs/v4/api/sql-sqlite-wasm) | +| [`@effect/ai-anthropic`](packages/ai/anthropic) | Anthropic provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-anthropic) | +| [`@effect/ai-openai`](packages/ai/openai) | OpenAI provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai) | +| [`@effect/ai-cloudflare`](packages/ai/cloudflare) | Cloudflare decision provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-cloudflare) | +| [`@effect/ai-typesafe`](packages/ai/typesafe) | TypeSafe decision provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-typesafe) | +| [`@effect/ai-openai-compat`](packages/ai/openai-compat) | OpenAI-compatible API provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai-compat) | +| [`@effect/ai-openrouter`](packages/ai/openrouter) | OpenRouter provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openrouter) | +| [`@effect/atom-react`](packages/atom/react) | React bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-react) | +| [`@effect/atom-solid`](packages/atom/solid) | SolidJS bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-solid) | +| [`@effect/atom-vue`](packages/atom/vue) | Vue bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-vue) | +| [`@effect/opentelemetry`](packages/opentelemetry) | [OpenTelemetry](https://opentelemetry.io) integration | [docs](https://effect.website/docs/v4/api/opentelemetry) | +| [`@effect/vitest`](packages/vitest) | Helpers for testing with [Vitest](https://vitest.dev) | [docs](https://effect.website/docs/v4/api/vitest) | +| [`@effect/docgen`](packages/tools/docgen) | Documentation generator for Effect projects | [docs](https://effect.website/docs/v4/api/docgen) | +| [`@effect/doctest`](packages/tools/doctest) | Runs JSDoc examples as Vitest tests | [docs](https://effect.website/docs/v4/api/doctest) | +| [`@effect/openapi-generator`](packages/tools/openapi-generator) | Generate Effect code from OpenAPI specifications | [docs](https://effect.website/docs/v4/api/openapi-generator) | + +## License + +MIT diff --git a/repos/effect/ai-docs/README.md b/ai-docs/README.md similarity index 100% rename from repos/effect/ai-docs/README.md rename to ai-docs/README.md diff --git a/repos/effect/ai-docs/package.json b/ai-docs/package.json similarity index 95% rename from repos/effect/ai-docs/package.json rename to ai-docs/package.json index 60c4a4afc..1c21dce10 100644 --- a/repos/effect/ai-docs/package.json +++ b/ai-docs/package.json @@ -27,8 +27,8 @@ "@effect/sql-sqlite-wasm": "workspace:*", "@effect/vitest": "workspace:*", "effect": "workspace:*", - "hono": "^4.13.9", - "nodemailer": "^10.0.10" + "hono": "^4.13.12", + "nodemailer": "^10.0.14" }, "devDependencies": { "@types/nodemailer": "^8.0.2" diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts b/ai-docs/src/01_effect/01_basics/01_effect-gen.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts rename to ai-docs/src/01_effect/01_basics/01_effect-gen.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts b/ai-docs/src/01_effect/01_basics/02_effect-fn.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts rename to ai-docs/src/01_effect/01_basics/02_effect-fn.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts b/ai-docs/src/01_effect/01_basics/10_creating-effects.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts rename to ai-docs/src/01_effect/01_basics/10_creating-effects.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/index.md b/ai-docs/src/01_effect/01_basics/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/index.md rename to ai-docs/src/01_effect/01_basics/index.md diff --git a/repos/effect/ai-docs/src/01_effect/02_schema/10_schema-basics.ts b/ai-docs/src/01_effect/02_schema/10_schema-basics.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/02_schema/10_schema-basics.ts rename to ai-docs/src/01_effect/02_schema/10_schema-basics.ts diff --git a/repos/effect/ai-docs/src/01_effect/02_schema/index.md b/ai-docs/src/01_effect/02_schema/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/02_schema/index.md rename to ai-docs/src/01_effect/02_schema/index.md diff --git a/repos/effect/ai-docs/src/01_effect/03_services/01_service.ts b/ai-docs/src/01_effect/03_services/01_service.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/01_service.ts rename to ai-docs/src/01_effect/03_services/01_service.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/10_reference.ts b/ai-docs/src/01_effect/03_services/10_reference.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/10_reference.ts rename to ai-docs/src/01_effect/03_services/10_reference.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts b/ai-docs/src/01_effect/03_services/20_layer-composition.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts rename to ai-docs/src/01_effect/03_services/20_layer-composition.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts b/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts rename to ai-docs/src/01_effect/03_services/20_layer-unwrap.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/index.md b/ai-docs/src/01_effect/03_services/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/index.md rename to ai-docs/src/01_effect/03_services/index.md diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/01_error-handling.ts b/ai-docs/src/01_effect/04_errors/01_error-handling.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/01_error-handling.ts rename to ai-docs/src/01_effect/04_errors/01_error-handling.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/10_catch-tags.ts b/ai-docs/src/01_effect/04_errors/10_catch-tags.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/10_catch-tags.ts rename to ai-docs/src/01_effect/04_errors/10_catch-tags.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/20_reason-errors.ts b/ai-docs/src/01_effect/04_errors/20_reason-errors.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/20_reason-errors.ts rename to ai-docs/src/01_effect/04_errors/20_reason-errors.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/index.md b/ai-docs/src/01_effect/04_errors/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/index.md rename to ai-docs/src/01_effect/04_errors/index.md diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/10_acquire-release.ts b/ai-docs/src/01_effect/05_resources/10_acquire-release.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/10_acquire-release.ts rename to ai-docs/src/01_effect/05_resources/10_acquire-release.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts b/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts rename to ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/30_layer-map.ts b/ai-docs/src/01_effect/05_resources/30_layer-map.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/30_layer-map.ts rename to ai-docs/src/01_effect/05_resources/30_layer-map.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/index.md b/ai-docs/src/01_effect/05_resources/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/index.md rename to ai-docs/src/01_effect/05_resources/index.md diff --git a/repos/effect/ai-docs/src/01_effect/06_running/10_run-main.ts b/ai-docs/src/01_effect/06_running/10_run-main.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/10_run-main.ts rename to ai-docs/src/01_effect/06_running/10_run-main.ts diff --git a/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts b/ai-docs/src/01_effect/06_running/20_layer-launch.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts rename to ai-docs/src/01_effect/06_running/20_layer-launch.ts diff --git a/repos/effect/ai-docs/src/01_effect/06_running/index.md b/ai-docs/src/01_effect/06_running/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/index.md rename to ai-docs/src/01_effect/06_running/index.md diff --git a/repos/effect/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts b/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts rename to ai-docs/src/01_effect/07_pubsub/10_pubsub.ts diff --git a/repos/effect/ai-docs/src/01_effect/07_pubsub/index.md b/ai-docs/src/01_effect/07_pubsub/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/07_pubsub/index.md rename to ai-docs/src/01_effect/07_pubsub/index.md diff --git a/repos/effect/ai-docs/src/03_stream/10_creating-streams.ts b/ai-docs/src/03_stream/10_creating-streams.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/10_creating-streams.ts rename to ai-docs/src/03_stream/10_creating-streams.ts diff --git a/repos/effect/ai-docs/src/03_stream/20_consuming-streams.ts b/ai-docs/src/03_stream/20_consuming-streams.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/20_consuming-streams.ts rename to ai-docs/src/03_stream/20_consuming-streams.ts diff --git a/repos/effect/ai-docs/src/03_stream/30_encoding.ts b/ai-docs/src/03_stream/30_encoding.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/30_encoding.ts rename to ai-docs/src/03_stream/30_encoding.ts diff --git a/repos/effect/ai-docs/src/03_stream/index.md b/ai-docs/src/03_stream/index.md similarity index 100% rename from repos/effect/ai-docs/src/03_stream/index.md rename to ai-docs/src/03_stream/index.md diff --git a/repos/effect/ai-docs/src/04_integration/10_managed-runtime.ts b/ai-docs/src/04_integration/10_managed-runtime.ts similarity index 100% rename from repos/effect/ai-docs/src/04_integration/10_managed-runtime.ts rename to ai-docs/src/04_integration/10_managed-runtime.ts diff --git a/repos/effect/ai-docs/src/04_integration/index.md b/ai-docs/src/04_integration/index.md similarity index 100% rename from repos/effect/ai-docs/src/04_integration/index.md rename to ai-docs/src/04_integration/index.md diff --git a/repos/effect/ai-docs/src/05_batching/10_request-resolver.ts b/ai-docs/src/05_batching/10_request-resolver.ts similarity index 100% rename from repos/effect/ai-docs/src/05_batching/10_request-resolver.ts rename to ai-docs/src/05_batching/10_request-resolver.ts diff --git a/repos/effect/ai-docs/src/05_batching/index.md b/ai-docs/src/05_batching/index.md similarity index 100% rename from repos/effect/ai-docs/src/05_batching/index.md rename to ai-docs/src/05_batching/index.md diff --git a/repos/effect/ai-docs/src/06_schedule/10_schedules.ts b/ai-docs/src/06_schedule/10_schedules.ts similarity index 100% rename from repos/effect/ai-docs/src/06_schedule/10_schedules.ts rename to ai-docs/src/06_schedule/10_schedules.ts diff --git a/repos/effect/ai-docs/src/06_schedule/index.md b/ai-docs/src/06_schedule/index.md similarity index 100% rename from repos/effect/ai-docs/src/06_schedule/index.md rename to ai-docs/src/06_schedule/index.md diff --git a/repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts b/ai-docs/src/07_datetime/10_creating-and-formatting.ts similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts rename to ai-docs/src/07_datetime/10_creating-and-formatting.ts diff --git a/repos/effect/ai-docs/src/07_datetime/20_time-zones.ts b/ai-docs/src/07_datetime/20_time-zones.ts similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/20_time-zones.ts rename to ai-docs/src/07_datetime/20_time-zones.ts diff --git a/repos/effect/ai-docs/src/07_datetime/index.md b/ai-docs/src/07_datetime/index.md similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/index.md rename to ai-docs/src/07_datetime/index.md diff --git a/repos/effect/ai-docs/src/08_observability/10_logging.ts b/ai-docs/src/08_observability/10_logging.ts similarity index 100% rename from repos/effect/ai-docs/src/08_observability/10_logging.ts rename to ai-docs/src/08_observability/10_logging.ts diff --git a/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts b/ai-docs/src/08_observability/20_otlp-tracing.ts similarity index 100% rename from repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts rename to ai-docs/src/08_observability/20_otlp-tracing.ts diff --git a/repos/effect/ai-docs/src/08_observability/index.md b/ai-docs/src/08_observability/index.md similarity index 100% rename from repos/effect/ai-docs/src/08_observability/index.md rename to ai-docs/src/08_observability/index.md diff --git a/repos/effect/ai-docs/src/09_testing/10_effect-tests.ts b/ai-docs/src/09_testing/10_effect-tests.ts similarity index 100% rename from repos/effect/ai-docs/src/09_testing/10_effect-tests.ts rename to ai-docs/src/09_testing/10_effect-tests.ts diff --git a/repos/effect/ai-docs/src/09_testing/20_layer-tests.ts b/ai-docs/src/09_testing/20_layer-tests.ts similarity index 100% rename from repos/effect/ai-docs/src/09_testing/20_layer-tests.ts rename to ai-docs/src/09_testing/20_layer-tests.ts diff --git a/repos/effect/ai-docs/src/09_testing/index.md b/ai-docs/src/09_testing/index.md similarity index 100% rename from repos/effect/ai-docs/src/09_testing/index.md rename to ai-docs/src/09_testing/index.md diff --git a/repos/effect/ai-docs/src/10_predicate/01_basics.ts b/ai-docs/src/10_predicate/01_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/10_predicate/01_basics.ts rename to ai-docs/src/10_predicate/01_basics.ts diff --git a/repos/effect/ai-docs/src/10_predicate/index.md b/ai-docs/src/10_predicate/index.md similarity index 100% rename from repos/effect/ai-docs/src/10_predicate/index.md rename to ai-docs/src/10_predicate/index.md diff --git a/repos/effect/ai-docs/src/40_sql/10_basics.ts b/ai-docs/src/40_sql/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/40_sql/10_basics.ts rename to ai-docs/src/40_sql/10_basics.ts diff --git a/repos/effect/ai-docs/src/40_sql/index.md b/ai-docs/src/40_sql/index.md similarity index 100% rename from repos/effect/ai-docs/src/40_sql/index.md rename to ai-docs/src/40_sql/index.md diff --git a/repos/effect/ai-docs/src/50_http-client/10_basics.ts b/ai-docs/src/50_http-client/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/50_http-client/10_basics.ts rename to ai-docs/src/50_http-client/10_basics.ts diff --git a/repos/effect/ai-docs/src/50_http-client/index.md b/ai-docs/src/50_http-client/index.md similarity index 100% rename from repos/effect/ai-docs/src/50_http-client/index.md rename to ai-docs/src/50_http-client/index.md diff --git a/repos/effect/ai-docs/src/51_http-server/10_basics.ts b/ai-docs/src/51_http-server/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/10_basics.ts rename to ai-docs/src/51_http-server/10_basics.ts diff --git a/repos/effect/ai-docs/src/51_http-server/20_testing.ts b/ai-docs/src/51_http-server/20_testing.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/20_testing.ts rename to ai-docs/src/51_http-server/20_testing.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts b/ai-docs/src/51_http-server/fixtures/api/Api.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts rename to ai-docs/src/51_http-server/fixtures/api/Api.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts b/ai-docs/src/51_http-server/fixtures/api/Authorization.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts rename to ai-docs/src/51_http-server/fixtures/api/Authorization.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts b/ai-docs/src/51_http-server/fixtures/api/System.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts rename to ai-docs/src/51_http-server/fixtures/api/System.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts b/ai-docs/src/51_http-server/fixtures/api/Users.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts rename to ai-docs/src/51_http-server/fixtures/api/Users.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts b/ai-docs/src/51_http-server/fixtures/domain/User.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts rename to ai-docs/src/51_http-server/fixtures/domain/User.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts b/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts rename to ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts b/ai-docs/src/51_http-server/fixtures/server/Authorization.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts rename to ai-docs/src/51_http-server/fixtures/server/Authorization.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts b/ai-docs/src/51_http-server/fixtures/server/Users.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts rename to ai-docs/src/51_http-server/fixtures/server/Users.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts b/ai-docs/src/51_http-server/fixtures/server/Users/http.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts rename to ai-docs/src/51_http-server/fixtures/server/Users/http.ts diff --git a/repos/effect/ai-docs/src/51_http-server/index.md b/ai-docs/src/51_http-server/index.md similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/index.md rename to ai-docs/src/51_http-server/index.md diff --git a/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts b/ai-docs/src/60_child-process/10_working-with-child-processes.ts similarity index 100% rename from repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts rename to ai-docs/src/60_child-process/10_working-with-child-processes.ts diff --git a/repos/effect/ai-docs/src/60_child-process/index.md b/ai-docs/src/60_child-process/index.md similarity index 100% rename from repos/effect/ai-docs/src/60_child-process/index.md rename to ai-docs/src/60_child-process/index.md diff --git a/repos/effect/ai-docs/src/70_cli/10_basics.ts b/ai-docs/src/70_cli/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/70_cli/10_basics.ts rename to ai-docs/src/70_cli/10_basics.ts diff --git a/repos/effect/ai-docs/src/70_cli/index.md b/ai-docs/src/70_cli/index.md similarity index 100% rename from repos/effect/ai-docs/src/70_cli/index.md rename to ai-docs/src/70_cli/index.md diff --git a/repos/effect/ai-docs/src/71_ai/10_language-model.ts b/ai-docs/src/71_ai/10_language-model.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/10_language-model.ts rename to ai-docs/src/71_ai/10_language-model.ts diff --git a/repos/effect/ai-docs/src/71_ai/20_tools.ts b/ai-docs/src/71_ai/20_tools.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/20_tools.ts rename to ai-docs/src/71_ai/20_tools.ts diff --git a/repos/effect/ai-docs/src/71_ai/30_chat.ts b/ai-docs/src/71_ai/30_chat.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/30_chat.ts rename to ai-docs/src/71_ai/30_chat.ts diff --git a/repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts b/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts rename to ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts diff --git a/repos/effect/ai-docs/src/71_ai/index.md b/ai-docs/src/71_ai/index.md similarity index 100% rename from repos/effect/ai-docs/src/71_ai/index.md rename to ai-docs/src/71_ai/index.md diff --git a/repos/effect/ai-docs/src/80_cluster/10_entities.ts b/ai-docs/src/80_cluster/10_entities.ts similarity index 100% rename from repos/effect/ai-docs/src/80_cluster/10_entities.ts rename to ai-docs/src/80_cluster/10_entities.ts diff --git a/repos/effect/ai-docs/src/80_cluster/index.md b/ai-docs/src/80_cluster/index.md similarity index 100% rename from repos/effect/ai-docs/src/80_cluster/index.md rename to ai-docs/src/80_cluster/index.md diff --git a/repos/effect/ai-docs/src/index.md b/ai-docs/src/index.md similarity index 100% rename from repos/effect/ai-docs/src/index.md rename to ai-docs/src/index.md diff --git a/repos/effect/ai-docs/tsconfig.json b/ai-docs/tsconfig.json similarity index 87% rename from repos/effect/ai-docs/tsconfig.json rename to ai-docs/tsconfig.json index e5be14e9b..8aad2d936 100644 --- a/repos/effect/ai-docs/tsconfig.json +++ b/ai-docs/tsconfig.json @@ -16,7 +16,8 @@ "namespaceImportPackages": [], "diagnosticSeverity": { "floatingEffect": "off", - "preferSchemaOverJson": "off" + "preferSchemaOverJson": "off", + "unstableApiUsage": "off" } } ] diff --git a/assets/dev/effect-app-apple-touch-180.png b/assets/dev/effect-app-apple-touch-180.png deleted file mode 100644 index 906d1621a05e8b5d53409bf02c9dd50b315b4681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11603 zcmYkicQl*t|37ZV3c8Hg9cmL|)GB?^s#TQQBZ^Rp5_=^ms#di|lp3}7-a=42R_(ni z_TKv&@6Y+2^E)}oegAQt=kxJg*L~f2JVG_!C{a@~Q4$akP%FPye2c$U+ARyo%P*!}Q%UGIf=6B8fG;G?$lo021A_@CiDTC=AIM`sjFU@Jh)sWZI>)1gyAl->MiKU5sSM|z`@B3y{oUdTq(9wTc(UY~>W(-;U;SJAw65Pjb3YCaCK^f$NY;vs%CzQo)q6?(3Yr9jHh-U3 zQ}_(JyZMzuUPDj78}s5;=Ba`3X~?V zbLObcnbMQv8SgD`hw0tEH$R_HlX=~J@4|MnF5TuTq^a_YN@O;Vkz8}b`2#(cz^zU3 z4nDeuBtQ$xdXO!F%aS=DZTS6TBH3Rfn_iqB`}wV*VB+&`4tWi0TJn0NQOw3{jos0x zwBx@do24-mCcxqEAErevE^&fdE$=Wazw-D#>KHBpK z8icLba@@KE$dr_Ie;~l$hGkc$b5&_TsZ|=KN7qizD)TiqvIyV0r!ugjb1SFb#@BZ0 z-A!QIreN+M%O3>=jM$)p!2bs1CCFg@;Nb0oS8ihTuxJn9ukINR6lvPk|z`)f$#&o%@w6)y2k=MDm2yUPG9-D;qZS$xl*vUeeR%qzaQATNx~m_fHad zmTc&p3aUu4aZDDr`88~1=0`QjzepMKP{oi!UOUv{$_r z626SloL51f6^)!ZeR{Q^8CK>z4o!g42fHX%6Pke|dcEYTDEUGT!k7vqUv*3_`H-fS z|D1+}4P}5a#u)3N0t*9K!$bO0;AYIk-~5QLs<{f}+0C z2Ww+kD!CG>&%nf~F+QsSlJ3dM!5K(Xm%}VwEn{trnu&Q|nXHvYMG_INLcO$MJ;a*b zLqYXFWu{d@ZC|Z#2De@x)4_y#lD>-_|NZ>?*??Ey1iB1aOAdMU4C|$^ccjtV*mQ4% zp197+dn5bq@lHw$5zfC^EhCf?6#^`eTNA4j{AvqCnto=SB`&`qMz3l-HOg!`J)D+-@9NV+#pX;2__i|SGxgKnXO8n=?e z6$N)CfXL#cl19?Z=Z1J|?=VSel?brXR?mnQ846tpzvPi2PW{J}jg)?9W=;qvgg+YV z^ap+0gXJEf2}X;HqI3Birkr0Du795aoPk$(HmG3v@7F2A5^~e@$hBXdu)}U%LZnKy z4zP+pU(29%8}P%q*<|ySHsV#Y@&4?1On&+t*m^3l(kgy)2;mwbV^8o*%u}x%Ix6SA zRsd*bacsjiC-G)0gSk$`sBq_=xR2fkRko1_4g{_v(h@t?!?uY$vqY06U<=eN5mSRr z))b=Jky(G+w2{P6cAC5j5AB#9;9jut0YsH6jDpG5efE^@{afiTDV zUd^I<4pkFE`1COmF8toB2Uw0AaaGMBN))6Uv<2omY&tUseER_S>eNaEFKWKcd0+s@ z-zoS@c#rP?YNypg9t4taen!lgs5>r?;_RQl9mnR@P5Vel&XPL;73%)+18~Qo1S1St zriEdtMk6KDKMxW@IA*<8iEu0%cy#x%1ns-cN&I!lTBIZoaUcl84ecvyM0$$00CP{A zMTv2bu-s4wAodRN?UDqYiwx&OW?Y6S8?2fA0X_J>SOZd0se?QOg!#g%d)raj_=4PK zOGC;Y3=U*)egL-K;ej4ky`sRoGNQ)uPiY@3_V7U8OvMbyy@dh>4+d^ya|ECs-czuK zdEdV+7gDm)v7!|i`k}rWfHiN&gJn&B0OLri;)4o8*|u)&Hi$uKsD+??gTmeBfIXOG zz}&5wbOzW%CItrY4};>*TM5hIQ2azLuY3H!0H5RCSdQ|Ud&4C0g9LyP^awIb)|U-i zGfI%7!m5!lWo;9rj<6HKk;eT=_Kvf<$+Epj z5J~>zvLyqDCtyqnYjbQQf5qDCM`e>*-Q`Glr2X@fet@H}K3#f0e-ErMd!J7oS!yS)%4C_Tg75QyNf-nEop5nqTG z7Xim@{)qe*Ae0V#LR$W>T4Pt+Djs5qGEp=piFZf*lYf3Z;`s%$%_=*MXf=%42oTZa z#=N}Pd@bo3XdtqGy2kt1u?&U7@FVdPtsPlI@R_~7Cr~m-3)enx?)D7#Z_RjrOF@fG zn&EP$*<~|t|Bnf?Ox6Qla`}cIcAa2VoUY*$Cn@VOxyF29u!3mt8+siK3xk$?0ZbF7(SkZ2p!q%mN)1YVx0K9kPO5u$v|8ZCkI^2ak_K>$D#u7{7Wdsilgv z|Euk&P9vcfySCDATi-Z*1~ODAUj0e}-jTCr(YuofpRH(M+ z^}^Wr(W{VSY&W?frtIh^DBCbh!n_!))KF2=&~WbhM++OiwwqemQ4rRR33JvT`vuwUu?NbH5EKDs$vf*?tzGB)3D zpC)T_?zhHomee-V`j2J1pM4FEF}hMGt415YU3SeU=sB1Q{Je1biQWjI!f60Ej!i_l zwV>gg5A%6~Od~_29oF{UoTWdPuIRZKF@woPLuH3E?@oP zd*Qj_TgvS9;``k+EsI|N2Y#`s0M?qs%S8$&zu(L`ZiV$oY@HPgCG7~exFIdv^o9nF zhCVIA6m8KI7jC@2>ZZ|^Lx&xARF_-p(NYro{(9hui5dI^0$)G?l!NhFhx{$-O~zl% zTM`)h)Z>USWP(Yd+^0wGlfrjmM9}B;3$c%~1ZdzJjEOx>2of?u#uNiTN0F;kC!cd^ zrM;oz=Px_9*?@ORm^r_%OWtc*GUDAgyNRD|T*|Uy%=)x%)=S0sjzFb)B9uLmlydvL zUyNgZ5wMj^L#puWC;)_!@tcgN&&(guM^VcKzws!rdHwL&muH)D*|AZ^;(-S@FhQjh znS=zY4zQDLsB^x*IZsSli7H@;D}Ed?%Shun_B?4fZA~pncj1V7W^-a9Gfa=Nb+k9J zK_o+ZxSVTo@rbV3>g@Z`xNbt#W3CRF3U;%8Ia{K_rH{$J9{CK~e88z;uY3nZqX#gC ze9PThga>m7yBz0X`_vO0X1nGnI86WwO-d!{`*=BnR4Y(W#_{`R$Ve07b%3E zR})Tg3!jv9O+J-71Y?mO7*KKQ$uO`NIsd&q!pcH1KWltEF|}1a$^kD#VdBw zbPLyBr2V%M8`I`w)W-GtFlB#ZV}JAeQA{DFz0#XEgj)(G4g#f@3j)Y}!RC40*5xuS z6t#BEsUyu&&5!Jk1y+uMM_|u$#;59z%e5gA=>?y#caEw-=-5?0-w4X}VjrQcfB=S+ zsW0|13EtzDvd<3v#R)P|zN;kN0^&k4rENlefs@tEI|ms+{M**S=?6382T#SguwVfb z9%>gBSX%ob$l|m`u5V@6eLGNO$h1T#^~(Nk8{t6*q+cIW@>%N_AtvJot6zAQmMlWzW!3K;~J#617z3SS{>nC@Hqf z#EmaJD%1+c4oOhwJ1JR-5KQ`c3Q^-RO0O=zQgwKS(N1pto84NjQP?V|r|&WR@4)~O zR?_ED=2!KLp<@3>EKfqstV7BLe|CUUekKxma?6Kd{0~l=b9qfY7sQhCHdr@|l3gEh zU5G4dU0KBLyYa=`Gc~RvoZ_|1QE!=`RoF6bO9&2YUa$gBS<5ylSha;@V9wowV?ha2Tu?o=WW+~^$52E^(z9Qx z*Q52BGaRWe=ToaS7AhlHKbqD*=6-Td(`r<5)_azRy*4AL`mAzBh<^rYAD3`b#lvLV zO2vadXCJ7d#XBA;v-cxJ(>ypR=;~(p=y~&5UV;Kfu0SKZ?*7Rngx2KQkJhLuF4B*( z{`3sI7WT$^`ZtSiTk0__DL+*uVyXq6lPm_Y-w9JDjgu{nEHg1*5Y~PxUr&RzIRL0= zG6kN9SkN)a-tiR9&}vK1V|dGCzJmN5nvr+*k$ z0K}MAL|b)W&vr&#nX{jMHt)Wh^4Wf901>4udakFcHVingS#0FS@bb&JsZiRC1%aqe z%$q(~oUH~O*nlj8zKP$lxvLrcdkeMJ&~Pc{X1}w``(ul92;4EnNiG*~H#C> z%>Q%w_?YyriTfa8vE{IqX+1uJqnrlD z)u7CT6>kA?h5F?m^Mi+sZst!knP5$(P8x-mS6`7;nNeQCN77jt$aa5Hc)K=lqSI?; zUzFmF9cCO1IR`I^Ft!grcYXMcK$YD-0FNr0FkmE|5`IX?%SD2CqMbC8dXBt*2t|UM8jb{-H2)bE;r1_B1^n=c}ly7!Ze31~dD+F#6N`$CYx@=z~MwY1n6F8%CI< zeLspiG2NGnJsg(mJ=_i#L^umny@MS;)W)0)|NeByYj~VkNgHQh?$$P%+S%a$K4T4H ze=rmtvqv2)X)Km~Bkemn4qW>A_(xXMi$85gDX%Fucc@@yzmwI6tfA)I%nAwNd{Gwd ze<&E=y-HG(v$>b;w{4>~HGK83Xw3J&Iq=2Ve;S3wIsg>|#_kp!@vO4iBL)qp9mh%6 z*Y`m#r$$O>YnmOIr8|d{tf8x+Q-kcNhi4J7fY!~WO$C8P5lJ|(Eg`J!9&blu3p^dA({wz*0Dh|6d`|o8=|1Z54^HAe1{4K zVsR>E4Rj7m=$m^|YQ(?!HOOjwGXH&QOrz^}aMDK+M$$Yz0eRSDZ;1ySxCY z6c_4#t^Vb5C)j-N(3ekGbFbM}&Yb?yPKbjHw#(NcGP;-UZm37LzXxvD!}&OZKKC7~ z&}M)K)gc4yOAtFZ&}w~`N;Tz4_b%JBFRCt~w!N;eWv%51P%ldISVsUMpg~zCHdPWO zTNiiTwQZLY75dhPBYnKGzhrBITXYsZvpdPX<-uVq(JaQf`}rwCnSb-1EubGysaDHX z`ja{MP*!JwWA9hI(4CKtE8t9>U9w;NJ7h4zW|-%*`}^^;?cv&C+k5q5#jKPv@Ht+# zdOOLiGR;-4=y!45K%?I}Oud1UT9NS$S4Vv^`OJdeRqoMwdaN?#1S8vQMK=rD{NB#+ zD_~K1!{sz=`?BrvaNeHQ1_p2H>`nbD`RrAiXRRB*CzLa4`fR21E-@A4_lH-y?ZK@q zSV~O0-mxX)42$R0$c$5zF&FCUcx`pfQgy|_IPor5apS&J_4Ve9I_dfbk9Q|s1JFq0 z(7Tw0iZGhxKT*A^z5EyZf6)1-LjoE^!R}U!GT3jz_G2EX);Y>H)vbCFpkw4m<4RVF zr#>505X_mGT;xeg6LON|)6l|FN!LcAoAZzuZ5e#U0cZtHwV1ZyFR+>iD~(m9xa~DJ zoFtvqF3TQu#7-j)?$7F^G&ZLy7FYKDO}vinvlgYpy)J9|Yk!tfJuvjcgR-9D?UtLz zSx{lLvP3XJd9uM6mH1(KNJ`E6>o+$Q+iF?^Ba{xwiMGEt$E)IRuCG%(?58$RD*fBs zJI$EYA<4iHgJOHRqkyx9o9VMJ_o!_aWjq(R-(Bvtcc9l~sY*1j(}n(wjpF;|Tc?EX zPa?R7dzZv@!wGPPHoaf6U|aVe}}~@ zt&)y?>)H?{DL1!+L_0Rip5Q`4Y%tkdkL>%w0C)tg>9dp*-%F0etKrwoUhdNDO?GsM zngwBvrb1cKED8JZlq2^uwrM2H3r4}z7c!~&^kk-vrlVJ+On6aBP~&F7s+f>E#?FEq{Jiu0gG1Hd$);J@oI4!P3|q;+x*&m<7}u5$6v1Lyq$; z&%6PrT;~SigAe3rh4A?+?dZ47G;LGkE?=tnHUpu+j(XgK>j~TU&@!(PdbT<{-{7=} z=**Mi<&nxn;TTX$0bnfw?LJuhc4<>c2pa^NpPj2XC`KTp<*q}N*0zh5arxE5UMvo$ z%?btjRRHbS_i4fY26`wB$vG;>`@PKT{>RnDi;*`rEZA>H5p+yckS{05yyNie!oNlc z^!LhqcNn9w*+iKeSJdp~Z;3rez@7!}J&F)>S=z(kJeqk1o}i2*>m;(V{#1 zi~X3!K3u-*at%sE>_6#YGg@mda8%t8n?DLXtDs_V?&iXUT-?82Xg1Pyni68Bf>^3e ziLt=srtkRHO2h!XIwH}R7w++;2S-J~){V!k^>sBH=hhSD)sMfhKCHvYy?$I_H53f@+TQqaJt7Zlqtfi^cS|yA~ff-dyraSC6r; z&rC_5T{Q_>^|X&Q^oc!o2P{!ba-f8aIt@~~Le93$st)Gle1NL4!m$yV-TE=YI-ygJ z#x2xxzEzEM5uLsg4H(}hv&9j;5T=LNg&~O#AOdWPF&;&RIsHxwnS){5PO%*g_D({T z9WVA&l_p}ovB}gyfAK7H<1D*ardw5LyECVA-f~G*`n0W@&Ac~^S(w{%cH*lC&qMui z2m$J5{eVjH;JXiM=hRa=tmyz8$7{~yHlIdK>!2N5vvAH;vr?VEuB&Ipc78?aV0MYh z?mvc#KLW1^W$g%I*=70z4JZaLhW&C+W^2AC`i;t<2(cE~etueT^SDkUd9-`cnS?|G zk=yoU4}{jfxeN&FOF_O90aRj|bm`d8_S6z47ov_IrHqjq{*+_uh_d1YP}@F!QS0>^j;+%qzmMDf;oG*Q#v# zJqX_u4OAA5ql<64`72iFwFh%{5?Wp;8T{lU*?pjb)1129X>XIvU!7Z=S$LcSDzy{q zWRQ6Q-V;+&oZObYk4kZVeWG=;et%hrX}VQ-?{I>}Tj&bm%*;Z`zeegyoOl23s)>;1 zmYXn-7VkSSkU2N^kCi(TjL3?B$cYIZd|694pEr2SvKqA8>U+s`FIo%c zujkNMN5px?^XQ-C5c2i1)_4_&IRH%qO3g0MI|$t<DM?ah0siVxC@r(2nex0-c04UIZ} z$0%10g&N-tCO??4Pr8iugx>f2*-B7~5uJ}}GHL4_6KV#3wEg9vZ4S_kXFU%8XPqyH z4bZe5%y-38&ZdO-e1`rYB8vFLjPss3^cn@|w<4NrJsh{f^QgB^J;R2nv7gk7M6~o{ zIx14ZIc~aSWCv~bNlwiT;}W{R<>yLker;Uy(3JbX%&epfdf{%Zk->Zv#$>|6lIw+- z*_7}lLR|bffxw&p5E7!-GFg3YR1uavSzFC&Dw&ld#C5Mspx64$I~OHIP3Cu#{wz*- z?Cj_|wQ8`Z|COTGo;Y-oQV#;%8*=MKN}7h7Q9JVXC#HzUgt~t=LJ$xDFK@K~=K72= z;CG;&oX?27fDbXHX#1X=Rr~bt+)EiN{&W^B|LPjA94yNWY&UfEqhleRp~~sp6AdIl zL{pdVXOlhF`@G93HY+1TpH&8a7g|`n{coL*9oiE-Q(O0a|2*IOG`?8&PM|>u<5NK3 z9(G}t02fuU*I15gYkfFi9)dwPjetXl2XJ06v+&K2y`Lpx=FL^DQvU#%I&`YYa)Kw` z3g}nZq{gPq-V2<__}5!IX?~e=X|C@G_P$nbd1t{ci6G2I{ANHgRKsW7*}8%aav8CH zmw7vJObZlh&omkqmd8%7_1G+x+hNoQMD$pPROg`VbA~1g#wbJTyvFKJ*e?B-L=S&Iv4!-beaLIh6X(wdQD9)EW z^@p~|=Nr0ypB=QbnX|W)&z9k9;NlxBcgoC<9h7My>$6=#TU#Omi06~%3jYNo5I%|+ zV8D5cx$1ji7YJ0aa-4i26|DQFfP*5E_BrrD5^xVkuIqZ{@C5KQtR1i$IA@S4c|ALp zJ*ZhXyZ*k<$Vac>?)L0*wOelQRF)5YW-nB*-%iUNsA5N<=|7vqrhMdoEdi{qP>+=O zGP1-tgERk9iS(A#soxuBRf z{P$W9|J+F4g^q4ObUty4gsdi{yPmYsjy3Q-rHAcaM}GK>O zKO3a)u3Tbf$GVzVD~hEY)F|^1I@NtoqAD<$pL{h0RN#3~2icPc>@}*@ zYnAqh@e49d4MXEL*XRMde^N;BsmE0{K=>sK?B(deByr#bqDZq}YVFP&Z=2@BeUBRN z9q=GkFNe?HCnh(V7E4e-$twmdX!$k2dtK6tAdsoM$1G^4I^Wn=`NWy$#`3K>TR@>d zhK25if=Rn6SBD%B*>s-lCo-vH<+yA86dJis+rwUC9f|Gmu;=hdmaBp(Tv!LxqbH6u z;%Dmx0)s$`AuOa=Wmc&Q%|-KkpZ<^SJ$IK2w<6;p9;hpgY@UWL=dnPhegq#yRckfN z2s zpZN5>$3jSSj=i%iC66?K3_eWLq!XG+K?1gJTlbAkw(ik14~A-@G;f{;OWK+h(I{{C z220MQDFjVu?4|;QUeRhKb2J{EkampR$MI8`)7*dzzzRS-1uKeOYj8rrKXwlNijE7G zOqOm)C%@+-E#2VtxwfouyC<^I{LS#I^S*dJujEKC{1J66=)c||$>9XOt#*uY-gvHW zV4&pGY(wE&Ri2?_AJouM=l0=jHm+O1>i5=k7>)(Rg3Ww>c$_TmfqK#KK{|K{V$Ce& zEDNtfdm4EjAF{*bI6;%c8(GT9qO*`OW*A99a^E@_Q*ggd+WX$b1}&ZhKhUm|_5x7A z*}X&#K7ZIVn!`&}xPP&YC7OiIzF=m-HqI7deJ1E>Sz(0X<|kP7V9D5YW$=o@z%x|& z5ECtYu1b^=)_LBpLI_tw5FV`hgTBw{f1t!p(D6e1%DI{@@p=cbiCZERFYovd^>8CJ z_El@rDqdP5w)sN>#;GE#nZu~d3XxgA(aSs9sArF8vIW}ml9QeWQkMGj^IamS66F+X_ZiS)v0dH zB^BP1)rCmUQ*-)DM?4PejV|nuH+)bw5SW$P7K!uxb}8~LdyQ3p|W z0PG##L97t~m+=66@(y35XaI!d{|N1Lb{N4@A@Tq6mVsyWl?1VOu+ElN+_wz23I4Ax z@+T>Qzmbx^bTKmf=72hCs0JbYEhTD*=P?@$IRoh2UX8qs+NC=O2@)OHqyggdb@~o7 zt(Sb42=cokWEM}h-=eev6My+At~lKNf2+9zL4=k;Y0#H${-oHwULsthGqn&F`93Hu z#xPniYY*%V+E+`*bNRK^J`2S*hy=cH!@{a3D)=FAb|-|J)xILZ&ND@xg}d&@&)UnB zop!m6#ReV#3nj4WCjzN~8;8}CM)ExZRx?Vzx&;n+-30Ey+V6NNju1MIYwZoF)whYs zmsDcfN!Xx(S6TOcKgaj&WP9s8`p41tD!E^z>v5LWqxWIHIoEg+gs>Mul`}eZS&fV{ z>JdgyRIZ{SWW?py^VSD*MC%({>#OTSvtAe=(bpkIgNq4iYUHhg?T#CaUDpf+cNJyC zMQYZAprCT zQqFYe%B~@_7ji_Y3~9zOKGpavdOm0l56DkXsg966lEq8aBujFUl1&8-#-!NZ5YYI5 z$@-<0=tIEat%6NAXNLiNV(LeD@C6C_RNQEHL^m_|;Bi9RWcdacQL!Zxz7lk_2Gf`ddc>>c~g-s&RsVPrUQnLpqY%Zf8p2`|Dq_!0n`)r>q^4kt#M zB261`|6B#PN%y`};q@vZbTF;28gpf9-;Y3KglGcl5|5D!N2Pz0YT8tI)yv6Gp>ba5 zw%+q!i_5yk@Se&3nItDo4m1%p*!0N9a)N-=^ky++6Mji2lUqty7x9&v zTd_}wvC!S&sP3hwjW?05@#ttLXv%K@TvJCd$^9h6IDW(5*JtSR=3n!soakh%hmB~0 z(3PDHxPx8So(;Pf@-C4K&gr3F38}vXG#h`ge}OEDj5Iyq-taTSNx z>*es9`mPHUJyZH!Q@M)`vZg^$r<%6(=D}FSbx>(EY*M@_GOLI_&Bg22&^M0Ycp{Ai zL;ja35fBnoWk_6S?8Bx4ua*MYia+a9KonndYzbdn@gUgR3{QK6^}JvFF@(%O4}QiL`kF4mLV4{CbfU;G!&<|3i!@osF5FYlIqU&6b&?drqAa6C)J~J!KyC^cB(xqxiX0KG*cu zHZ5g691s-oH9-{=*doJv6rR9WcrK9otLufiP{%Pbjtxp%(n{yX$7vK(@)r0(2}>7j q@f?0UCRp)5{cjg{M%w-jVO-tjJ-f<7AYLU+p#1WUV$pxlfd2 `OpenAiTelemetry.AllAttributes`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. +- `OpenAiTelemetry.AllAttributes` -> `none`: Removed. Use OpenAiTelemetry.OpenAiTelemetryAttributeOptions for the grouped options accepted by OpenAiTelemetry.addGenAIAnnotations, or OpenAiTelemetry.OpenAiTelemetryAttributes for the prefixed span attribute keys. - `OpenAiTelemetry.OpenAiTelemetryAttributeOptions` -> `OpenAiTelemetry.OpenAiTelemetryAttributeOptions`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. +- `OpenAiTelemetry.RequestAttributes` -> `OpenAiTelemetry.RequestAttributes`: Attributes are written under openai.request.\* instead of gen\_ai.openai.request.\*. responseFormat was removed; LanguageModel records gen\_ai.output.type for every provider. + +- `OpenAiTelemetry.WellKnownResponseFormat` -> `Telemetry.WellKnownOutputType`: The response format is no longer a telemetry option. LanguageModel records gen\_ai.output.type as text or json from the requested response format. + - `OpenAiTelemetry.addGenAIAnnotations` -> `OpenAiTelemetry.addGenAIAnnotations`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. ### `@effect/ai-openai/OpenAiTokenizer` @@ -5064,6 +5068,18 @@ effect/rpc/Utils (barrel: effect/rpc) - `Response.urlSourcePart` -> `Response.makePart("source", { ...params, sourceType: "url" })`: The lowercase convenience constructor was removed. The UrlSourcePart model remains, and the generic constructor now requires the URL source discriminator. +### `@effect/ai/Telemetry` + +- `Telemetry.AllAttributes` -> `none`: Removed. Use Telemetry.GenAITelemetryAttributeOptions for the grouped options accepted by Telemetry.addGenAIAnnotations, or Telemetry.GenAITelemetryAttributes for the prefixed gen\_ai.\* span attribute keys. + +- `Telemetry.BaseAttributes` -> `Telemetry.ProviderAttributes`: gen\_ai.system was replaced by gen\_ai.provider.name. Pass provider: { name } instead of system; legacy system values are not mapped. + +- `Telemetry.GenAITelemetryAttributeOptions` -> `Telemetry.GenAITelemetryAttributeOptions`: The system and token options were removed. Use provider: { name } for gen\_ai.provider.name; gen\_ai.token.type has no span replacement. The new output option writes gen\_ai.output.type. + +- `Telemetry.TokenAttributes` -> `none`: Removed with the token option. gen\_ai.token.type is not a span attribute in the current OpenTelemetry GenAI conventions. + +- `Telemetry.WellKnownSystem` -> `Telemetry.WellKnownProviderName`: Values follow gen\_ai.provider.name: az.ai.inference, az.ai.openai, gemini, vertex\_ai and xai became azure.ai.inference, azure.ai.openai, gcp.gemini, gcp.vertex\_ai and x\_ai. + ### `@effect/ai/Tool` - `Tool.AnyParametersSchema` -> `Schema.Constraint`: The AI-specific alias was removed. V4 Tool parameter schemas use the general Schema.Constraint type and are no longer restricted to the old struct-or-EmptyParams union. @@ -7956,6 +7972,12 @@ effect/rpc/Utils (barrel: effect/rpc) - `SqliteClient.SqliteClient` -> `@effect/sql-sqlite-bun/SqliteClient#SqliteClient`: Retained; the service value is now a Context.Service. +- `SqliteClient.layer` -> `SqliteClient.layer`: The layer now has a SqlError error channel for database opening and initialization failures. Handle setup failures, or use Layer.orDie when they should remain fatal. + +- `SqliteClient.layerConfig` -> `SqliteClient.layerConfig`: The layer now fails with ConfigError | SqlError: configuration errors remain typed, and database opening and initialization failures are also typed. Handle SqlError as well, or use Layer.orDie if all setup failures should be fatal. + +- `SqliteClient.make` -> `SqliteClient.make`: Database opening and initialization now fail with SqlError instead of defects. Handle the typed setup error, or use Effect.orDie when setup failures should remain fatal. + ### `@effect/sql-sqlite-do/SqliteClient` - `SqliteClient.SqliteClientConfig` -> `@effect/sql-sqlite-do/SqliteClient#SqliteClientConfig`: Retained; db is optional and storage may be supplied, but one of db or storage is required at runtime. @@ -7966,6 +7988,12 @@ effect/rpc/Utils (barrel: effect/rpc) - `SqliteClient.SqliteClientConfig` -> `@effect/sql-sqlite-node/SqliteClient#SqliteClientConfig`: Retained; prepareCacheTTL now uses Duration.Input. +- `SqliteClient.layer` -> `SqliteClient.layer`: The layer now has a SqlError error channel for database opening and initialization failures. Handle setup failures, or use Layer.orDie when they should remain fatal. + +- `SqliteClient.layerConfig` -> `SqliteClient.layerConfig`: The layer now fails with ConfigError | SqlError: configuration errors remain typed, and database opening and initialization failures are also typed. Handle SqlError as well, or use Layer.orDie if all setup failures should be fatal. + +- `SqliteClient.make` -> `SqliteClient.make`: Database opening and initialization now fail with SqlError instead of defects. Handle the typed setup error, or use Effect.orDie when setup failures should remain fatal. + ### `@effect/sql-sqlite-react-native/SqliteClient` - `SqliteClient.asyncQuery` -> `@effect/sql-sqlite-react-native/SqliteClient#AsyncQuery`: Renamed and changed from FiberRef to Context.Reference; prefer withAsyncQuery or provide AsyncQuery as a service. diff --git a/repos/effect/migration/yieldable.md b/migration/yieldable.md similarity index 100% rename from repos/effect/migration/yieldable.md rename to migration/yieldable.md diff --git a/old.patch b/old.patch deleted file mode 100644 index e69de29bb..000000000 diff --git a/package.json b/package.json index 957a67a38..572fb0d81 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,84 @@ { "private": true, - "packageManager": "pnpm@11.18.0", - "engines": { - "pnpm": ">= 11.0.0" - }, + "type": "module", + "packageManager": "pnpm@11.20.0", "scripts": { - "all": "pnpm -r", - "pub": "pnpm ci:version && git commit -am 'chore: update versions' && pnpm pub:only", - "pub:only": "pnpm ci:publish && git push && git push --follow-tags", - "ci:version": "pnpm changeset version", - "ci:publish": "pnpm build && pnpm changeset publish", - "preinstall": "npx only-allow pnpm", - "clean": "pnpm all clean", - "clean-dist": "pnpm -r clean-dist", - "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 --no-bail test:run", - "pr:ready": "node scripts/agent-pr-ready.mjs", - "testsuite": "pnpm -r --no-bail testsuite", - "up-all": "pnpm --recursive update", - "u": "pnpm run update && pnpm i && pnpm dedupe", - "update": "ncu -u && pnpm -r exec ncu -u", - "ncu": "ncu", - "packages": "pnpm -r --workspace-concurrency 1", - "circular-packages": "pnpm packages circular", - "lint-packages": "pnpm packages lint", - "test-packages": "pnpm packages test:run", - "testsuite-packages": "pnpm packages testsuite", - "watch-packages": "pnpm packages build && pnpm packages watch", - "build:tsc": "pnpm check && node scripts/rewrite-dist-declaration-imports.mjs", - "check": "tsgo --build ./tsconfig.all.json", - "watch": "pnpm build:tsc --watch", - "build": "cd packages/eslint-shared-config && pnpm build && cd ../.. && cd packages/cli && pnpm build && cd .. && pnpm build:tsc && cd vue-components && pnpm build", - "rbuild": "pnpm clean && pnpm build", - "nnm": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && pnpm i", - "prepare": "node -e \"const v=require('typescript/package.json').version;if(v.startsWith('6.')){require('child_process').execSync('effect-language-service patch',{stdio:'inherit'})}else{console.log('Skipping effect-language-service patch for TypeScript '+v)}\" && effect-tsgo patch --no-typescript --oxlint && node scripts/install-git-hooks.mjs", - "subtree:effect": "node scripts/sync-effect-subtree.js" - }, - "dependencies": { - "date-fns": "^4.4.0", - "fast-check": "^4.9.0", - "vue": "^3.5.35" + "prepare": "node scripts/setup-agents.mjs && effect-tsgo patch", + "clean": "node scripts/clean.mjs", + "codegen": "pnpm --recursive --parallel --filter \"./packages/**/*\" run codegen", + "codemod": "node scripts/codemod.mjs", + "build": "tsc -b tsconfig.packages.json && pnpm --recursive --parallel --filter \"./packages/**/*\" run build && node scripts/copy-ai-docs.mjs", + "bundle-analyze": "bash scripts/bundle-analyze.sh", + "bundle-compare": "bash scripts/bundle-compare.sh", + "bundle-compare-selected": "bash scripts/bundle-compare-selected.sh", + "circular": "node scripts/circular.mjs", + "test": "vitest", + "test-cluster": "vitest run --project cluster-integration", + "doctest": "vitest --config vitest.docs.ts", + "coverage": "vitest --coverage", + "check": "tsc -b tsconfig.json", + "typeperf": "pnpm --dir packages/effect exec node typeperf/run.mjs", + "typeperf-compare": "pnpm --dir packages/effect exec node typeperf/compare.mjs", + "runtimeperf": "pnpm --dir packages/effect exec node runtimeperf/run.mts", + "runtimeperf-compare": "pnpm --dir packages/effect exec node runtimeperf/compare.mts", + "check-recursive": "pnpm --recursive --filter \"./packages/**/*\" exec tsc -b tsconfig.json", + "check-dist-types": "node scripts/check-dist-types.mjs", + "jsdocs": "effect-jsdocs", + "lint": "oxlint -f unix && dprint check", + "lint-fix": "oxlint --fix && dprint fmt", + "ai-docgen": "effect-ai-docgen ai-docs/src -o LLMS.md", + "ai-docgen:watch": "pnpm ai-docgen --watch", + "api-diff": "pnpm --dir packages/tools/api-diff exec node src/bin.ts", + "test-types": "tstyche --target '>=5.9'", + "changeset-version": "changeset version && node scripts/version.mjs", + "changeset-publish": "node scripts/set-strip-internal.mjs && pnpm codemod && pnpm build && changeset publish" }, "devDependencies": { - "@changesets/cli": "^2.31.0", - "@effect-app/cli": "2.1.0-beta.35", - "@effect-app/eslint-codegen-model": "workspace:*", - "@effect-app/infra": "workspace:*", - "@effect/language-service": "0.87.3", - "@effect/tsgo": "^0.47.2", - "@effect/platform-node": "4.0.1", - "@effect/vitest": "4.0.1", - "@tsconfig/strictest": "^2.0.8", - "@types/node": "25.9.1", - "@typescript-eslint/eslint-plugin": "8.60.0", - "@typescript-eslint/parser": "8.60.0", - "@typescript/native-preview": "7.0.0-dev.20260626.1", - "dprint": "^0.54.0", - "effect": "4.0.1", - "effect-app": "workspace:*", - "eslint": "^10.4.1", - "json5": "^2.2.3", - "lint-staged": "^17.3.0", + "@babel/cli": "^8.0.6", + "@babel/core": "^8.0.6", + "@babel/plugin-transform-export-namespace-from": "^8.0.1", + "@babel/plugin-transform-modules-commonjs": "^8.0.1", + "@changesets/changelog-github": "1.0.1", + "@changesets/cli": "3.0.3", + "@effect/ai-docgen": "workspace:^", + "@effect/bundle": "workspace:^", + "@effect/docgen": "workspace:^", + "@effect/doctest": "workspace:^", + "@effect/jsdocs": "workspace:^", + "@effect/oxc": "workspace:^", + "@effect/tsgo": "^0.51.0", + "@effect/utils": "workspace:^", + "@effect/vitest": "workspace:^", + "@faker-js/faker": "^10.6.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^6.0.3", + "@rollup/plugin-terser": "^1.0.0", + "@types/jscodeshift": "^17.3.0", + "@types/node": "^26.6.4", + "@vitest/coverage-v8": "^5.0.3", + "@vitest/web-worker": "^5.0.3", + "ast-types": "^0.16.3", + "babel-plugin-annotate-pure-calls": "^0.5.0", + "dprint": "^0.59.0", + "fast-check": "^4.10.2", + "glob": "^13.0.6", + "happy-dom": "^20.14.5", + "jscodeshift": "^17.4.0", + "lalph": "^0.3.139", "madge": "^8.0.0", - "npm-check-updates": "^22.2.1", - "oxlint": "1.82.0", - "oxlint-tsgolint": "7.0.2001", - "pkg-pr-new": "0.0.75", - "ts-plugin-sort-import-suggestions": "^1.0.4", - "typescript": "~6.0.3", - "vite": "^8.0.15", - "vitest": "^4.1.7" + "oxlint": "^1.86.0", + "pkg-pr-new": "0.0.88", + "playwright": "^1.63.0", + "rollup": "^4.64.0", + "rollup-plugin-bundle-stats": "^4.22.3", + "rollup-plugin-esbuild": "^6.2.1", + "rollup-plugin-visualizer": "^7.1.1", + "terser": "^5.51.2", + "tstyche": "^7.2.5", + "typescript": "^7.0.2", + "vite": "^8.3.2", + "vitest": "^5.0.3", + "vitest-websocket-mock": "^0.8.0", + "zod": "^4.6.5" } } diff --git a/repos/effect/packages/ai/anthropic/CHANGELOG.md b/packages/ai/anthropic/CHANGELOG.md similarity index 94% rename from repos/effect/packages/ai/anthropic/CHANGELOG.md rename to packages/ai/anthropic/CHANGELOG.md index f209b0986..ccd63f7aa 100644 --- a/repos/effect/packages/ai/anthropic/CHANGELOG.md +++ b/packages/ai/anthropic/CHANGELOG.md @@ -1,5 +1,22 @@ # @effect/ai-anthropic +## 4.0.2 + +### Patch Changes + +- [#8870](https://github.com/Effect-TS/effect/pull/8870) [`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1) Thanks @Makisuo! - Update AI tracing to use current OpenTelemetry GenAI conventions: + + - Replace `gen_ai.system` with `gen_ai.provider.name`. The `system` option, `BaseAttributes` and `WellKnownSystem` are removed in favor of `provider: { name }`, `ProviderAttributes` and `WellKnownProviderName`. + - Remove `gen_ai.token.type`, which is not a span attribute in the current conventions, along with the `token` option, `TokenAttributes` and the `AllAttributes` types. + - `LanguageModel` spans are client spans and record `gen_ai.output.type` (`text` or `json`) for every provider. The OpenAI `responseFormat` telemetry option and `WellKnownResponseFormat` are removed. + - Move OpenAI service-tier and system-fingerprint attributes from `gen_ai.openai.*` to `openai.*`. + - Rename `concurrency`, `toolChoice` and `objectName` span attributes to `effect.ai.concurrency`, `effect.ai.tool_choice` and `effect.ai.object_name`. Tool choices record the resolved value, defaulting to `"auto"`; object choices are JSON-encoded. + - Include cached tokens in Anthropic `gen_ai.usage.input_tokens` and report cache reads and writes as `gen_ai.usage.cache_read.input_tokens` and `gen_ai.usage.cache_write.input_tokens`. The duplicate Anthropic `cacheCreationInputTokens` and `cacheReadInputTokens` response options are removed. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/ai/anthropic/README.md b/packages/ai/anthropic/README.md similarity index 100% rename from repos/effect/packages/ai/anthropic/README.md rename to packages/ai/anthropic/README.md diff --git a/repos/effect/packages/ai/anthropic/codegen.yml b/packages/ai/anthropic/codegen.yml similarity index 100% rename from repos/effect/packages/ai/anthropic/codegen.yml rename to packages/ai/anthropic/codegen.yml diff --git a/repos/effect/packages/ai/anthropic/package.json b/packages/ai/anthropic/package.json similarity index 98% rename from repos/effect/packages/ai/anthropic/package.json rename to packages/ai/anthropic/package.json index fc63c76f4..eb3b2c36a 100644 --- a/repos/effect/packages/ai/anthropic/package.json +++ b/packages/ai/anthropic/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-anthropic", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "An Anthropic provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts b/packages/ai/anthropic/src/AnthropicClient.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicClient.ts rename to packages/ai/anthropic/src/AnthropicClient.ts diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts b/packages/ai/anthropic/src/AnthropicConfig.ts similarity index 96% rename from repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts rename to packages/ai/anthropic/src/AnthropicConfig.ts index 8c41f4314..f3f8122ca 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts +++ b/packages/ai/anthropic/src/AnthropicConfig.ts @@ -4,6 +4,7 @@ * `AnthropicClient` when request helpers run, so code can add middleware, * logging, or other client changes without rebuilding the client layer. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -21,6 +22,7 @@ import type { HttpClient } from "effect/http/HttpClient" * * @see {@link withClientTransform} for scoping an HTTP client transformation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -42,6 +44,7 @@ export class AnthropicConfig extends Context.Service< /** * Namespace containing types associated with the `AnthropicConfig` service. * + * @stability unstable * @since 4.0.0 */ export declare namespace AnthropicConfig { @@ -68,6 +71,7 @@ export declare namespace AnthropicConfig { * Use when you need to apply a temporary `HttpClient` transformation, such as adding middleware or logging, to a * specific scope of an effectful program. * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicError.ts b/packages/ai/anthropic/src/AnthropicError.ts similarity index 98% rename from repos/effect/packages/ai/anthropic/src/AnthropicError.ts rename to packages/ai/anthropic/src/AnthropicError.ts index 08d9abe2b..ad3b68b73 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicError.ts +++ b/packages/ai/anthropic/src/AnthropicError.ts @@ -4,6 +4,7 @@ * Provides Anthropic-specific metadata fields for AI error types through module * augmentation, enabling typed access to Anthropic error details. * + * @stability unstable * @since 4.0.0 */ @@ -18,6 +19,7 @@ * * @see {@link AnthropicRateLimitMetadata} for rate-limit responses that also include parsed Anthropic rate-limit headers * + * @stability unstable * @category models * @since 4.0.0 */ @@ -39,6 +41,7 @@ export type AnthropicErrorMetadata = { * * Extends base error metadata with rate limit-specific information from Anthropic's rate limit headers. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts b/packages/ai/anthropic/src/AnthropicLanguageModel.ts similarity index 99% rename from repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts rename to packages/ai/anthropic/src/AnthropicLanguageModel.ts index 251009d5c..3709ddec4 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts +++ b/packages/ai/anthropic/src/AnthropicLanguageModel.ts @@ -2817,7 +2817,7 @@ const annotateRequest = ( request: typeof Generated.BetaCreateMessageParams.Encoded ): void => { addGenAIAnnotations(span, { - system: "anthropic", + provider: { name: "anthropic" }, operation: { name: "chat" }, request: { model: request.model, @@ -2833,6 +2833,8 @@ const annotateRequest = ( } const annotateResponse = (span: Span, response: Generated.BetaMessage): void => { + const cacheRead = response.usage.cache_read_input_tokens ?? 0 + const cacheWrite = response.usage.cache_creation_input_tokens ?? 0 addGenAIAnnotations(span, { response: { id: response.id, @@ -2840,10 +2842,11 @@ const annotateResponse = (span: Span, response: Generated.BetaMessage): void => finishReasons: response.stop_reason ? [response.stop_reason] : undefined }, usage: { - inputTokens: response.usage.input_tokens, + inputTokens: response.usage.input_tokens + cacheRead + cacheWrite, outputTokens: response.usage.output_tokens } }) + annotateCacheUsage(span, cacheRead, cacheWrite) } const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) => { @@ -2861,13 +2864,20 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) => finishReasons: [part.reason] }, usage: { - inputTokens: part.usage.inputTokens.uncached, + inputTokens: part.usage.inputTokens.total, outputTokens: part.usage.outputTokens.total } }) + annotateCacheUsage(span, part.usage.inputTokens.cacheRead, part.usage.inputTokens.cacheWrite) } } +// Cache counts are included in `gen_ai.usage.input_tokens`. +const annotateCacheUsage = (span: Span, cacheRead: number | undefined, cacheWrite: number | undefined): void => { + if (Predicate.isNotNullish(cacheRead)) span.attribute("gen_ai.usage.cache_read.input_tokens", cacheRead) + if (Predicate.isNotNullish(cacheWrite)) span.attribute("gen_ai.usage.cache_write.input_tokens", cacheWrite) +} + // ============================================================================= // Internal Utilities // ============================================================================= diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts b/packages/ai/anthropic/src/AnthropicTelemetry.ts similarity index 87% rename from repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts rename to packages/ai/anthropic/src/AnthropicTelemetry.ts index 93fe32612..672deb2b3 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts +++ b/packages/ai/anthropic/src/AnthropicTelemetry.ts @@ -4,6 +4,7 @@ * standard GenAI telemetry attributes and adds request and response metadata * under the `gen_ai.anthropic.*` OpenTelemetry namespaces. * + * @stability unstable * @since 4.0.0 */ import * as Telemetry from "effect/ai/Telemetry" @@ -22,6 +23,7 @@ import type { Simplify } from "effect/Types" * conventions: * https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/ * + * @stability unstable * @category models * @since 4.0.0 */ @@ -31,19 +33,11 @@ export type AnthropicTelemetryAttributes = Simplify< & Telemetry.AttributesWithPrefix > -/** - * All telemetry attributes which are part of the GenAI specification, - * including the Anthropic-specific attributes. - * - * @category models - * @since 4.0.0 - */ -export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes - /** * Telemetry attributes which are part of the GenAI specification and are * namespaced by `gen_ai.anthropic.request`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -62,6 +56,7 @@ export interface RequestAttributes { * Telemetry attributes which are part of the GenAI specification and are * namespaced by `gen_ai.anthropic.response`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -70,19 +65,12 @@ export interface ResponseAttributes { * The stop reason from the response. */ readonly stopReason?: string | null | undefined - /** - * Number of cache creation input tokens. - */ - readonly cacheCreationInputTokens?: number | null | undefined - /** - * Number of cache read input tokens. - */ - readonly cacheReadInputTokens?: number | null | undefined } /** * Options accepted by `addGenAIAnnotations`, combining standard GenAI telemetry attributes with optional Anthropic request and response attributes. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -113,6 +101,7 @@ const addAnthropicResponseAttributes = Telemetry.addSpanAttributes("gen_ai.anthr * * This method mutates the `Span` in place. * + * @stability unstable * @category tracing * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicTool.ts b/packages/ai/anthropic/src/AnthropicTool.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicTool.ts rename to packages/ai/anthropic/src/AnthropicTool.ts diff --git a/repos/effect/packages/ai/anthropic/src/Generated.ts b/packages/ai/anthropic/src/Generated.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/Generated.ts rename to packages/ai/anthropic/src/Generated.ts diff --git a/repos/effect/packages/ai/anthropic/src/index.ts b/packages/ai/anthropic/src/index.ts similarity index 88% rename from repos/effect/packages/ai/anthropic/src/index.ts rename to packages/ai/anthropic/src/index.ts index 9882f693f..844df9eb1 100644 --- a/repos/effect/packages/ai/anthropic/src/index.ts +++ b/packages/ai/anthropic/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,11 +12,13 @@ export * as AnthropicClient from "./AnthropicClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as AnthropicConfig from "./AnthropicConfig.ts" /** + * @stability unstable * @since 4.0.0 */ export * as AnthropicError from "./AnthropicError.ts" @@ -27,6 +30,7 @@ export * as AnthropicError from "./AnthropicError.ts" export * as AnthropicLanguageModel from "./AnthropicLanguageModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as AnthropicTelemetry from "./AnthropicTelemetry.ts" diff --git a/repos/effect/packages/ai/anthropic/src/internal/errors.ts b/packages/ai/anthropic/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/internal/errors.ts rename to packages/ai/anthropic/src/internal/errors.ts diff --git a/repos/effect/packages/ai/anthropic/src/internal/utilities.ts b/packages/ai/anthropic/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/internal/utilities.ts rename to packages/ai/anthropic/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts b/packages/ai/anthropic/test/AnthropicClient.test.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts rename to packages/ai/anthropic/test/AnthropicClient.test.ts diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts b/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts similarity index 91% rename from repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts rename to packages/ai/anthropic/test/AnthropicLanguageModel.test.ts index 50e37c586..5f240511d 100644 --- a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +++ b/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts @@ -1,6 +1,7 @@ import { AnthropicClient, AnthropicLanguageModel, AnthropicTool } from "@effect/ai-anthropic" import { assert, describe, it } from "@effect/vitest" -import { Cause, Effect, Exit, Layer, Option, Redacted, Schema, Stream } from "effect" +import { assertDefined } from "@effect/vitest/utils" +import { Cause, Effect, Exit, Layer, Option, Redacted, Schema, Stream, Tracer } from "effect" import { type AiError, AnthropicStructuredOutput, @@ -14,6 +15,74 @@ import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientRes describe("AnthropicLanguageModel", () => { describe("streamText", () => { + it.effect("reports total input tokens and cache usage on the span", () => + Effect.gen(function*() { + const spans: Array = [] + const tracer = Tracer.make({ + span(options) { + const span = new Tracer.NativeSpan(options) + spans.push(span) + return span + } + }) + const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.succeed(sseResponse(request, [ + { + type: "message_start", + message: { + id: "msg_test_1", + type: "message", + role: "assistant", + model: "claude-sonnet-4-20250514", + content: [], + stop_reason: null, + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: 20, + cache_read_input_tokens: 30, + input_tokens: 10, + output_tokens: 0, + service_tier: null + } + } + }, + { + type: "message_delta", + delta: { stop_reason: "end_turn", stop_sequence: null }, + usage: { + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + input_tokens: null, + output_tokens: 5 + } + }, + { type: "message_stop" } + ])) + ) + )) + ) + + const parts = yield* LanguageModel.streamText({ prompt: "Hello" }).pipe( + Stream.runCollect, + Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")), + Effect.provide(layer), + Effect.withTracer(tracer) + ) + const finish = parts.find((part) => part.type === "finish") + assertDefined(finish) + assert.deepStrictEqual(finish.usage.inputTokens, { uncached: 10, total: 60, cacheRead: 30, cacheWrite: 20 }) + const span = spans.find((span) => span.name === "LanguageModel.streamText") + assertDefined(span) + assert.strictEqual(span.attributes.get("gen_ai.usage.input_tokens"), 60) + assert.strictEqual(span.attributes.get("gen_ai.usage.cache_read.input_tokens"), 30) + assert.strictEqual(span.attributes.get("gen_ai.usage.cache_write.input_tokens"), 20) + assert.strictEqual(span.attributes.get("gen_ai.usage.output_tokens"), 5) + })) + for ( const [label, geo] of [ ["missing", {}], @@ -637,6 +706,56 @@ describe("AnthropicLanguageModel", () => { })) } + it.effect("reports total input tokens and cache usage on the span", () => + Effect.gen(function*() { + const spans: Array = [] + const tracer = Tracer.make({ + span(options) { + const span = new Tracer.NativeSpan(options) + spans.push(span) + return span + } + }) + const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.succeed(jsonResponse(request, { + id: "msg_test_1", + type: "message", + role: "assistant", + model: "claude-sonnet-4-20250514", + content: [{ type: "text", text: "Hello" }], + stop_reason: "end_turn", + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: 20, + cache_read_input_tokens: 30, + input_tokens: 10, + output_tokens: 5, + service_tier: null + } + })) + ) + )) + ) + + yield* LanguageModel.generateText({ prompt: "Hello" }).pipe( + Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")), + Effect.provide(layer), + Effect.withTracer(tracer) + ) + const span = spans.find((span) => span.name === "LanguageModel.generateText") + assertDefined(span) + const attributes = span.attributes + assert.strictEqual(attributes.get("gen_ai.provider.name"), "anthropic") + assert.strictEqual(attributes.get("gen_ai.usage.input_tokens"), 60) + assert.strictEqual(attributes.get("gen_ai.usage.cache_read.input_tokens"), 30) + assert.strictEqual(attributes.get("gen_ai.usage.cache_write.input_tokens"), 20) + assert.strictEqual(attributes.get("gen_ai.usage.output_tokens"), 5) + })) + it.effect("omits strictJsonSchema from the request while preserving tool strictness", () => Effect.gen(function*() { let capturedRequest: HttpClientRequest.HttpClientRequest | undefined = undefined diff --git a/repos/effect/packages/ai/anthropic/tsconfig.json b/packages/ai/anthropic/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/anthropic/tsconfig.json rename to packages/ai/anthropic/tsconfig.json diff --git a/repos/effect/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts b/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts rename to packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts diff --git a/packages/ai/cloudflare/CHANGELOG.md b/packages/ai/cloudflare/CHANGELOG.md new file mode 100644 index 000000000..87470785c --- /dev/null +++ b/packages/ai/cloudflare/CHANGELOG.md @@ -0,0 +1,17 @@ +# @effect/ai-cloudflare + +## 4.0.2 + +### Patch Changes + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + +## 4.0.1 + +### Patch Changes + +- [#8677](https://github.com/Effect-TS/effect/pull/8677) [`480bba2`](https://github.com/Effect-TS/effect/commit/480bba2dfebba5b0e58695a9837895dfb65895ee) Thanks @danieljvdm! - Add a Cloudflare Workers AI decision provider for Clef and Clef-flash. +- Updated dependencies [[`ed2cc2f`](https://github.com/Effect-TS/effect/commit/ed2cc2f322dfd24da550c8d0ac811c3130b79d74), [`58a2ee8`](https://github.com/Effect-TS/effect/commit/58a2ee8e0db8e0a90bae83c6cceeff6916772fee), [`bc44526`](https://github.com/Effect-TS/effect/commit/bc445262eeaaba6f7f28a154023a590e193e547d), [`073bb47`](https://github.com/Effect-TS/effect/commit/073bb475d1e84e09cdcd8303a4a558f2a73d8396), [`2ec021e`](https://github.com/Effect-TS/effect/commit/2ec021eff7fbd2206adc3e8bf4e803ed4bf07d6a), [`cad1118`](https://github.com/Effect-TS/effect/commit/cad111867a666cebe8ff9bf2237a997fa6e4cb14), [`1672d9c`](https://github.com/Effect-TS/effect/commit/1672d9ca8da3e5b536bf7afb73365cccc6c01f53), [`a81e4e6`](https://github.com/Effect-TS/effect/commit/a81e4e6cb3dfbf01b1c53c1c877fddeaced8a278), [`f5c2a91`](https://github.com/Effect-TS/effect/commit/f5c2a91621401a018b770446d35ef99806c5561f), [`498e72b`](https://github.com/Effect-TS/effect/commit/498e72ba14d0191d6bf66cc0e931c229a1ff442f), [`cabf123`](https://github.com/Effect-TS/effect/commit/cabf1230723e92684a4b0b836ccfde25c657f9f1), [`a5b1d4e`](https://github.com/Effect-TS/effect/commit/a5b1d4e4cae01dbe30890d3796f846bda00ad2ae), [`878cb33`](https://github.com/Effect-TS/effect/commit/878cb33f4f5f5a31d2c80a9ddb7d62a611230e1c), [`5e6f756`](https://github.com/Effect-TS/effect/commit/5e6f7568a4c895eab3087249b191c5dcba5e73ef), [`18dfb98`](https://github.com/Effect-TS/effect/commit/18dfb98cf6d2aeedbab8ef5734f9bc273a82d56e), [`04706ef`](https://github.com/Effect-TS/effect/commit/04706eff5da94b4d475b99eab8b0256bed97b080), [`f157482`](https://github.com/Effect-TS/effect/commit/f15748273a89afff4b05d5e6bec22c567c2a3cda), [`b5a2d4c`](https://github.com/Effect-TS/effect/commit/b5a2d4c1d62c9620a68d72b7f20248c69ef7663b), [`1c18c68`](https://github.com/Effect-TS/effect/commit/1c18c688df10b697c10283160dba143e049f3d50), [`43416c4`](https://github.com/Effect-TS/effect/commit/43416c4142f973f4988440716eb54a498659cd3a), [`d6ac8f9`](https://github.com/Effect-TS/effect/commit/d6ac8f923a33ff298a87f31230735e71d2d082e9), [`1d9fb8c`](https://github.com/Effect-TS/effect/commit/1d9fb8c540a11326650baa0866a17951665189e7), [`20dcd50`](https://github.com/Effect-TS/effect/commit/20dcd5060581077b46325a433f787ba10a56963d), [`42e5750`](https://github.com/Effect-TS/effect/commit/42e5750f7e2fbf349cfe45452d7cc9dce98d6fe9), [`888e326`](https://github.com/Effect-TS/effect/commit/888e326a3180aea9b117fe7c97aace5dd527596e), [`0b52bb4`](https://github.com/Effect-TS/effect/commit/0b52bb42fac5aecb8bcb8d899293c641b62c3f66), [`e823e69`](https://github.com/Effect-TS/effect/commit/e823e693773aa163c0081af00a5236118446c6ae), [`962647b`](https://github.com/Effect-TS/effect/commit/962647b6a16000f882e6999f001c513a8cccc711), [`43fdcdc`](https://github.com/Effect-TS/effect/commit/43fdcdc6c81b6ad22d54061d6c6c45d9243d7c33), [`1f89b8e`](https://github.com/Effect-TS/effect/commit/1f89b8ec64e6362da3eaaff8cbb235df88d3441c), [`e1a521a`](https://github.com/Effect-TS/effect/commit/e1a521a6886a281abee016ff9aa57c3b172057c8)]: + - effect@4.0.1 diff --git a/repos/effect/packages/ai/cloudflare/README.md b/packages/ai/cloudflare/README.md similarity index 100% rename from repos/effect/packages/ai/cloudflare/README.md rename to packages/ai/cloudflare/README.md diff --git a/repos/effect/packages/ai/cloudflare/package.json b/packages/ai/cloudflare/package.json similarity index 98% rename from repos/effect/packages/ai/cloudflare/package.json rename to packages/ai/cloudflare/package.json index 56b7826f8..b7e707957 100644 --- a/repos/effect/packages/ai/cloudflare/package.json +++ b/packages/ai/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-cloudflare", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A Cloudflare Workers AI decision provider for Effect", diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareClient.ts b/packages/ai/cloudflare/src/CloudflareClient.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/CloudflareClient.ts rename to packages/ai/cloudflare/src/CloudflareClient.ts diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareDecisionModel.ts b/packages/ai/cloudflare/src/CloudflareDecisionModel.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/CloudflareDecisionModel.ts rename to packages/ai/cloudflare/src/CloudflareDecisionModel.ts diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareSchema.ts b/packages/ai/cloudflare/src/CloudflareSchema.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/CloudflareSchema.ts rename to packages/ai/cloudflare/src/CloudflareSchema.ts diff --git a/repos/effect/packages/ai/cloudflare/src/index.ts b/packages/ai/cloudflare/src/index.ts similarity index 94% rename from repos/effect/packages/ai/cloudflare/src/index.ts rename to packages/ai/cloudflare/src/index.ts index bcabe60b4..7b7411acc 100644 --- a/repos/effect/packages/ai/cloudflare/src/index.ts +++ b/packages/ai/cloudflare/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/cloudflare/src/internal/errors.ts b/packages/ai/cloudflare/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/internal/errors.ts rename to packages/ai/cloudflare/src/internal/errors.ts diff --git a/repos/effect/packages/ai/cloudflare/test/CloudflareClient.test.ts b/packages/ai/cloudflare/test/CloudflareClient.test.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/test/CloudflareClient.test.ts rename to packages/ai/cloudflare/test/CloudflareClient.test.ts diff --git a/repos/effect/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts b/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts rename to packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts diff --git a/repos/effect/packages/ai/cloudflare/tsconfig.json b/packages/ai/cloudflare/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/cloudflare/tsconfig.json rename to packages/ai/cloudflare/tsconfig.json diff --git a/repos/effect/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts b/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts rename to packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts diff --git a/repos/effect/packages/ai/openai-compat/CHANGELOG.md b/packages/ai/openai-compat/CHANGELOG.md similarity index 94% rename from repos/effect/packages/ai/openai-compat/CHANGELOG.md rename to packages/ai/openai-compat/CHANGELOG.md index a1238e469..d588c5c9a 100644 --- a/repos/effect/packages/ai/openai-compat/CHANGELOG.md +++ b/packages/ai/openai-compat/CHANGELOG.md @@ -1,5 +1,22 @@ # @effect/ai-openai-compat +## 4.0.2 + +### Patch Changes + +- [#8870](https://github.com/Effect-TS/effect/pull/8870) [`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1) Thanks @Makisuo! - Update AI tracing to use current OpenTelemetry GenAI conventions: + + - Replace `gen_ai.system` with `gen_ai.provider.name`. The `system` option, `BaseAttributes` and `WellKnownSystem` are removed in favor of `provider: { name }`, `ProviderAttributes` and `WellKnownProviderName`. + - Remove `gen_ai.token.type`, which is not a span attribute in the current conventions, along with the `token` option, `TokenAttributes` and the `AllAttributes` types. + - `LanguageModel` spans are client spans and record `gen_ai.output.type` (`text` or `json`) for every provider. The OpenAI `responseFormat` telemetry option and `WellKnownResponseFormat` are removed. + - Move OpenAI service-tier and system-fingerprint attributes from `gen_ai.openai.*` to `openai.*`. + - Rename `concurrency`, `toolChoice` and `objectName` span attributes to `effect.ai.concurrency`, `effect.ai.tool_choice` and `effect.ai.object_name`. Tool choices record the resolved value, defaulting to `"auto"`; object choices are JSON-encoded. + - Include cached tokens in Anthropic `gen_ai.usage.input_tokens` and report cache reads and writes as `gen_ai.usage.cache_read.input_tokens` and `gen_ai.usage.cache_write.input_tokens`. The duplicate Anthropic `cacheCreationInputTokens` and `cacheReadInputTokens` response options are removed. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/ai/openai-compat/README.md b/packages/ai/openai-compat/README.md similarity index 100% rename from repos/effect/packages/ai/openai-compat/README.md rename to packages/ai/openai-compat/README.md diff --git a/repos/effect/packages/ai/openai-compat/package.json b/packages/ai/openai-compat/package.json similarity index 98% rename from repos/effect/packages/ai/openai-compat/package.json rename to packages/ai/openai-compat/package.json index eff372e60..361b19cb8 100644 --- a/repos/effect/packages/ai/openai-compat/package.json +++ b/packages/ai/openai-compat/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai-compat", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "An OpenAI compat integration for Effect", diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts b/packages/ai/openai-compat/src/OpenAiClient.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts rename to packages/ai/openai-compat/src/OpenAiClient.ts diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts b/packages/ai/openai-compat/src/OpenAiConfig.ts similarity index 96% rename from repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts rename to packages/ai/openai-compat/src/OpenAiConfig.ts index 91766a29d..8e8099eb6 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts +++ b/packages/ai/openai-compat/src/OpenAiConfig.ts @@ -4,6 +4,7 @@ * tool-calling code can use this scoped configuration to add middleware, * instrumentation, or routing without rebuilding the client layer. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -22,6 +23,7 @@ import type { HttpClient } from "effect/http/HttpClient" * * @see {@link withClientTransform} for scoping an HTTP client transformation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -43,6 +45,7 @@ export class OpenAiConfig extends Context.Service< /** * Types associated with the `OpenAiConfig` context service. * + * @stability unstable * @since 4.0.0 */ export declare namespace OpenAiConfig { @@ -71,6 +74,7 @@ export declare namespace OpenAiConfig { * OpenAI-compatible provider services read the transform from the * `OpenAiConfig` context. * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts b/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts rename to packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts b/packages/ai/openai-compat/src/OpenAiError.ts similarity index 98% rename from repos/effect/packages/ai/openai-compat/src/OpenAiError.ts rename to packages/ai/openai-compat/src/OpenAiError.ts index e89e40c41..ad317c58e 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +++ b/packages/ai/openai-compat/src/OpenAiError.ts @@ -12,12 +12,14 @@ * makes those shapes available on the corresponding shared AI error metadata * interfaces without defining new runtime error classes. * + * @stability unstable * @since 4.0.0 */ /** * OpenAI-specific error metadata fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -44,6 +46,7 @@ export type OpenAiErrorMetadata = { * Extends base error metadata with rate limit specific information from * OpenAI's rate limit headers. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts b/packages/ai/openai-compat/src/OpenAiLanguageModel.ts similarity index 99% rename from repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts rename to packages/ai/openai-compat/src/OpenAiLanguageModel.ts index 3feeed9c9..b8ba73d30 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +++ b/packages/ai/openai-compat/src/OpenAiLanguageModel.ts @@ -1353,7 +1353,7 @@ const annotateRequest = ( request: CreateResponseRequestJson ): void => { addGenAIAnnotations(span, { - system: "openai", + provider: { name: "openai" }, operation: { name: "chat" }, request: { model: request.model as string, @@ -1363,7 +1363,6 @@ const annotateRequest = ( }, openai: { request: { - responseFormat: request.response_format?.type, serviceTier: request.service_tier as string | undefined } } diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts b/packages/ai/openai-compat/src/OpenAiTelemetry.ts similarity index 68% rename from repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts rename to packages/ai/openai-compat/src/OpenAiTelemetry.ts index e2c4af08e..040b26185 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts +++ b/packages/ai/openai-compat/src/OpenAiTelemetry.ts @@ -1,9 +1,10 @@ /** * The `OpenAiTelemetry` module defines OpenAI-compatible telemetry attributes * and a helper for adding them to a tracing span. It keeps the standard GenAI - * telemetry attributes and adds request and response metadata under the - * `gen_ai.openai.*` OpenTelemetry namespaces. + * telemetry attributes and adds service tier and system fingerprint under + * `openai.*`. * + * @stability unstable * @since 4.0.0 */ import * as Telemetry from "effect/ai/Telemetry" @@ -22,36 +23,24 @@ import type { Simplify } from "effect/Types" * conventions: * https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/ * + * @stability unstable * @category models * @since 4.0.0 */ export type OpenAiTelemetryAttributes = Simplify< & Telemetry.GenAITelemetryAttributes - & Telemetry.AttributesWithPrefix - & Telemetry.AttributesWithPrefix + & Telemetry.AttributesWithPrefix + & Telemetry.AttributesWithPrefix > /** - * All telemetry attributes which are part of the GenAI specification, - * including the OpenAI-specific attributes. - * - * @category models - * @since 4.0.0 - */ -export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes - -/** - * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai.openai.request`. + * OpenAI request metadata, written under `openai.request.*`. * + * @stability unstable * @category models * @since 4.0.0 */ export interface RequestAttributes { - /** - * The response format that is requested. - */ - readonly responseFormat?: (string & {}) | WellKnownResponseFormat | null | undefined /** * The service tier requested. May be a specific tier, `default`, or `auto`. */ @@ -60,8 +49,9 @@ export interface RequestAttributes { /** * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai.openai.response`. + * namespaced by `openai.response`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -78,21 +68,7 @@ export interface ResponseAttributes { } /** - * The `gen_ai.openai.request.response_format` attribute has a list of - * well-known values. - * - * **Details** - * - * If one of them applies, then the respective value **MUST** be used; - * otherwise, a custom value **MAY** be used. - * - * @category models - * @since 4.0.0 - */ -export type WellKnownResponseFormat = "json_object" | "json_schema" | "text" - -/** - * The `gen_ai.openai.request.service_tier` attribute has a list of + * The `openai.request.service_tier` attribute has a list of * well-known values. * * **Details** @@ -100,6 +76,7 @@ export type WellKnownResponseFormat = "json_object" | "json_schema" | "text" * If one of them applies, then the respective value **MUST** be used; * otherwise, a custom value **MAY** be used. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -109,6 +86,7 @@ export type WellKnownServiceTier = "auto" | "default" * Options accepted by `addGenAIAnnotations`, combining standard GenAI telemetry * attributes with optional OpenAI-compatible request and response attributes. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -119,10 +97,10 @@ export type OpenAiTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeO } | undefined } -const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("gen_ai.openai.request", String.camelToSnake)< +const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("openai.request", String.camelToSnake)< RequestAttributes > -const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.response", String.camelToSnake)< +const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("openai.response", String.camelToSnake)< ResponseAttributes > @@ -137,14 +115,14 @@ const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.r * * **Details** * - * Standard GenAI attributes are applied first. When OpenAI request or response - * metadata is present, it is written under `gen_ai.openai.request.*` and - * `gen_ai.openai.response.*` attributes. + * Standard GenAI attributes are applied first. OpenAI metadata is written under + * `openai.request.*` and `openai.response.*`. * * **Gotchas** * * Mutates the supplied `Span` in place. * + * @stability unstable * @category tracing * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai-compat/src/index.ts b/packages/ai/openai-compat/src/index.ts similarity index 87% rename from repos/effect/packages/ai/openai-compat/src/index.ts rename to packages/ai/openai-compat/src/index.ts index 3b5e53712..3d5650c40 100644 --- a/repos/effect/packages/ai/openai-compat/src/index.ts +++ b/packages/ai/openai-compat/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,6 +12,7 @@ export * as OpenAiClient from "./OpenAiClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiConfig from "./OpenAiConfig.ts" @@ -22,6 +24,7 @@ export * as OpenAiConfig from "./OpenAiConfig.ts" export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiError from "./OpenAiError.ts" @@ -33,6 +36,7 @@ export * as OpenAiError from "./OpenAiError.ts" export * as OpenAiLanguageModel from "./OpenAiLanguageModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiTelemetry from "./OpenAiTelemetry.ts" diff --git a/repos/effect/packages/ai/openai-compat/src/internal/errors.ts b/packages/ai/openai-compat/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/internal/errors.ts rename to packages/ai/openai-compat/src/internal/errors.ts diff --git a/repos/effect/packages/ai/openai-compat/src/internal/utilities.ts b/packages/ai/openai-compat/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/internal/utilities.ts rename to packages/ai/openai-compat/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts b/packages/ai/openai-compat/test/OpenAiClient.test.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts rename to packages/ai/openai-compat/test/OpenAiClient.test.ts diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts b/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts rename to packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts b/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts rename to packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts diff --git a/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts b/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts new file mode 100644 index 000000000..8ee03ff7a --- /dev/null +++ b/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts @@ -0,0 +1,24 @@ +import { OpenAiTelemetry } from "@effect/ai-openai-compat" +import { assert, describe, it } from "@effect/vitest" +import { Effect } from "effect" + +describe("OpenAiTelemetry", () => { + it.effect("writes current OpenAI GenAI attribute keys", () => + Effect.gen(function*() { + const span = yield* Effect.currentSpan + OpenAiTelemetry.addGenAIAnnotations(span, { + provider: { name: "openai" }, + openai: { + request: { serviceTier: "auto" }, + response: { serviceTier: "default", systemFingerprint: "fp_1" } + } + }) + const attributes = Object.fromEntries(span.attributes) + assert.deepStrictEqual(attributes, { + "gen_ai.provider.name": "openai", + "openai.request.service_tier": "auto", + "openai.response.service_tier": "default", + "openai.response.system_fingerprint": "fp_1" + }) + }).pipe(Effect.withSpan("test"))) +}) diff --git a/repos/effect/packages/ai/openai-compat/tsconfig.json b/packages/ai/openai-compat/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openai-compat/tsconfig.json rename to packages/ai/openai-compat/tsconfig.json diff --git a/repos/effect/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts b/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts rename to packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts diff --git a/repos/effect/packages/ai/openai/CHANGELOG.md b/packages/ai/openai/CHANGELOG.md similarity index 94% rename from repos/effect/packages/ai/openai/CHANGELOG.md rename to packages/ai/openai/CHANGELOG.md index 00ead7db7..e7637f174 100644 --- a/repos/effect/packages/ai/openai/CHANGELOG.md +++ b/packages/ai/openai/CHANGELOG.md @@ -1,5 +1,22 @@ # @effect/ai-openai +## 4.0.2 + +### Patch Changes + +- [#8870](https://github.com/Effect-TS/effect/pull/8870) [`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1) Thanks @Makisuo! - Update AI tracing to use current OpenTelemetry GenAI conventions: + + - Replace `gen_ai.system` with `gen_ai.provider.name`. The `system` option, `BaseAttributes` and `WellKnownSystem` are removed in favor of `provider: { name }`, `ProviderAttributes` and `WellKnownProviderName`. + - Remove `gen_ai.token.type`, which is not a span attribute in the current conventions, along with the `token` option, `TokenAttributes` and the `AllAttributes` types. + - `LanguageModel` spans are client spans and record `gen_ai.output.type` (`text` or `json`) for every provider. The OpenAI `responseFormat` telemetry option and `WellKnownResponseFormat` are removed. + - Move OpenAI service-tier and system-fingerprint attributes from `gen_ai.openai.*` to `openai.*`. + - Rename `concurrency`, `toolChoice` and `objectName` span attributes to `effect.ai.concurrency`, `effect.ai.tool_choice` and `effect.ai.object_name`. Tool choices record the resolved value, defaulting to `"auto"`; object choices are JSON-encoded. + - Include cached tokens in Anthropic `gen_ai.usage.input_tokens` and report cache reads and writes as `gen_ai.usage.cache_read.input_tokens` and `gen_ai.usage.cache_write.input_tokens`. The duplicate Anthropic `cacheCreationInputTokens` and `cacheReadInputTokens` response options are removed. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/ai/openai/README.md b/packages/ai/openai/README.md similarity index 100% rename from repos/effect/packages/ai/openai/README.md rename to packages/ai/openai/README.md diff --git a/repos/effect/packages/ai/openai/codegen.yaml b/packages/ai/openai/codegen.yaml similarity index 100% rename from repos/effect/packages/ai/openai/codegen.yaml rename to packages/ai/openai/codegen.yaml diff --git a/repos/effect/packages/ai/openai/package.json b/packages/ai/openai/package.json similarity index 98% rename from repos/effect/packages/ai/openai/package.json rename to packages/ai/openai/package.json index 2d4f59645..c041c153b 100644 --- a/repos/effect/packages/ai/openai/package.json +++ b/packages/ai/openai/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "An OpenAI provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/openai/src/Generated.ts b/packages/ai/openai/src/Generated.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/Generated.ts rename to packages/ai/openai/src/Generated.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiClient.ts b/packages/ai/openai/src/OpenAiClient.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiClient.ts rename to packages/ai/openai/src/OpenAiClient.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts b/packages/ai/openai/src/OpenAiClientGenerated.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts rename to packages/ai/openai/src/OpenAiClientGenerated.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiConfig.ts b/packages/ai/openai/src/OpenAiConfig.ts similarity index 96% rename from repos/effect/packages/ai/openai/src/OpenAiConfig.ts rename to packages/ai/openai/src/OpenAiConfig.ts index 4257c98db..deecf9722 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiConfig.ts +++ b/packages/ai/openai/src/OpenAiConfig.ts @@ -4,6 +4,7 @@ * model, and embedding code read this scoped transform when they execute * provider calls. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -21,6 +22,7 @@ import type { HttpClient } from "effect/http/HttpClient" * * @see {@link withClientTransform} for scoping an HTTP client transformation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -42,6 +44,7 @@ export class OpenAiConfig extends Context.Service< /** * Types used by the `OpenAiConfig` context service. * + * @stability unstable * @since 4.0.0 */ export declare namespace OpenAiConfig { @@ -77,6 +80,7 @@ export declare namespace OpenAiConfig { * If a transform is already present in the scoped config, this helper replaces * it. Compose transforms manually when both should apply. * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts b/packages/ai/openai/src/OpenAiEmbeddingModel.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts rename to packages/ai/openai/src/OpenAiEmbeddingModel.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiError.ts b/packages/ai/openai/src/OpenAiError.ts similarity index 98% rename from repos/effect/packages/ai/openai/src/OpenAiError.ts rename to packages/ai/openai/src/OpenAiError.ts index a0568d63b..1f777293e 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiError.ts +++ b/packages/ai/openai/src/OpenAiError.ts @@ -4,12 +4,14 @@ * Provides OpenAI-specific metadata fields for AI error types through module * augmentation, enabling typed access to OpenAI error details. * + * @stability unstable * @since 4.0.0 */ /** * OpenAI-specific error metadata fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -36,6 +38,7 @@ export type OpenAiErrorMetadata = { * Extends base error metadata with rate limit specific information from * OpenAI's rate limit headers. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts b/packages/ai/openai/src/OpenAiLanguageModel.ts similarity index 99% rename from repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts rename to packages/ai/openai/src/OpenAiLanguageModel.ts index 9758884f3..f84ac6377 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts +++ b/packages/ai/openai/src/OpenAiLanguageModel.ts @@ -2827,7 +2827,7 @@ const annotateRequest = ( request: typeof OpenAiSchema.CreateResponse.Encoded ): void => { addGenAIAnnotations(span, { - system: "openai", + provider: { name: "openai" }, operation: { name: "chat" }, request: { model: request.model as string, @@ -2837,7 +2837,6 @@ const annotateRequest = ( }, openai: { request: { - responseFormat: (request.text as any)?.format?.type, serviceTier: request.service_tier as string | undefined } } diff --git a/repos/effect/packages/ai/openai/src/OpenAiSchema.ts b/packages/ai/openai/src/OpenAiSchema.ts similarity index 99% rename from repos/effect/packages/ai/openai/src/OpenAiSchema.ts rename to packages/ai/openai/src/OpenAiSchema.ts index 97a704915..f88b492fd 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiSchema.ts +++ b/packages/ai/openai/src/OpenAiSchema.ts @@ -918,6 +918,7 @@ const ResponseError = Schema.Struct({ * * Cache misses report a reason and token estimates, separate from response usage. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -946,6 +947,7 @@ export const PromptCacheDiagnostics = Schema.Union([ /** * Prompt cache comparison result reported by the OpenAI Responses API. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts b/packages/ai/openai/src/OpenAiTelemetry.ts similarity index 70% rename from repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts rename to packages/ai/openai/src/OpenAiTelemetry.ts index f5c1e7400..a420da54c 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts +++ b/packages/ai/openai/src/OpenAiTelemetry.ts @@ -1,10 +1,10 @@ /** * The `OpenAiTelemetry` module defines OpenAI-specific telemetry attributes * and a helper for adding them to a tracing span. It keeps the standard GenAI - * telemetry attributes and adds request and response metadata, such as response - * format, service tier, and system fingerprint, under the `gen_ai.openai.*` - * OpenTelemetry namespaces. + * telemetry attributes and adds service tier and system fingerprint under + * `openai.*`. * + * @stability unstable * @since 4.0.0 */ import * as Telemetry from "effect/ai/Telemetry" @@ -23,36 +23,24 @@ import type { Simplify } from "effect/Types" * conventions: * https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/ * + * @stability unstable * @category models * @since 4.0.0 */ export type OpenAiTelemetryAttributes = Simplify< & Telemetry.GenAITelemetryAttributes - & Telemetry.AttributesWithPrefix - & Telemetry.AttributesWithPrefix + & Telemetry.AttributesWithPrefix + & Telemetry.AttributesWithPrefix > /** - * All telemetry attributes which are part of the GenAI specification, - * including the OpenAI-specific attributes. - * - * @category models - * @since 4.0.0 - */ -export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes - -/** - * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai.openai.request`. + * OpenAI request metadata, written under `openai.request.*`. * + * @stability unstable * @category models * @since 4.0.0 */ export interface RequestAttributes { - /** - * The response format that is requested. - */ - readonly responseFormat?: (string & {}) | WellKnownResponseFormat | null | undefined /** * The service tier requested. May be a specific tier, `default`, or `auto`. */ @@ -61,8 +49,9 @@ export interface RequestAttributes { /** * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai.openai.response`. + * namespaced by `openai.response`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -79,21 +68,7 @@ export interface ResponseAttributes { } /** - * The `gen_ai.openai.request.response_format` attribute has the following - * list of well-known values. - * - * **Details** - * - * If one of them applies, then the respective value **MUST** be used; - * otherwise, a custom value **MAY** be used. - * - * @category models - * @since 4.0.0 - */ -export type WellKnownResponseFormat = "json_object" | "json_schema" | "text" - -/** - * The `gen_ai.openai.request.service_tier` attribute has the following + * The `openai.request.service_tier` attribute has the following * list of well-known values. * * **Details** @@ -101,6 +76,7 @@ export type WellKnownResponseFormat = "json_object" | "json_schema" | "text" * If one of them applies, then the respective value **MUST** be used; * otherwise, a custom value **MAY** be used. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -110,6 +86,7 @@ export type WellKnownServiceTier = "auto" | "default" * Options accepted by `addGenAIAnnotations`, combining standard GenAI * telemetry attributes with optional OpenAI request and response attributes. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -120,10 +97,10 @@ export type OpenAiTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeO } | undefined } -const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("gen_ai.openai.request", String.camelToSnake)< +const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("openai.request", String.camelToSnake)< RequestAttributes > -const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.response", String.camelToSnake)< +const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("openai.response", String.camelToSnake)< ResponseAttributes > @@ -143,6 +120,7 @@ const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.r * @see {@link OpenAiTelemetryAttributeOptions} for the accepted telemetry attributes * @see {@link Telemetry.addGenAIAnnotations} for the provider-neutral annotation helper * + * @stability unstable * @category tracing * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiTool.ts b/packages/ai/openai/src/OpenAiTool.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiTool.ts rename to packages/ai/openai/src/OpenAiTool.ts diff --git a/repos/effect/packages/ai/openai/src/index.ts b/packages/ai/openai/src/index.ts similarity index 91% rename from repos/effect/packages/ai/openai/src/index.ts rename to packages/ai/openai/src/index.ts index 77389e360..69d7e26bd 100644 --- a/repos/effect/packages/ai/openai/src/index.ts +++ b/packages/ai/openai/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -23,6 +24,7 @@ export * as OpenAiClient from "./OpenAiClient.ts" export * as OpenAiClientGenerated from "./OpenAiClientGenerated.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiConfig from "./OpenAiConfig.ts" @@ -34,6 +36,7 @@ export * as OpenAiConfig from "./OpenAiConfig.ts" export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiError from "./OpenAiError.ts" @@ -51,6 +54,7 @@ export * as OpenAiLanguageModel from "./OpenAiLanguageModel.ts" export * as OpenAiSchema from "./OpenAiSchema.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiTelemetry from "./OpenAiTelemetry.ts" diff --git a/repos/effect/packages/ai/openai/src/internal/errors.ts b/packages/ai/openai/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/internal/errors.ts rename to packages/ai/openai/src/internal/errors.ts diff --git a/repos/effect/packages/ai/openai/src/internal/utilities.ts b/packages/ai/openai/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/internal/utilities.ts rename to packages/ai/openai/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts b/packages/ai/openai/test/OpenAiClient.test.ts similarity index 100% rename from repos/effect/packages/ai/openai/test/OpenAiClient.test.ts rename to packages/ai/openai/test/OpenAiClient.test.ts diff --git a/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts b/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts rename to packages/ai/openai/test/OpenAiEmbeddingModel.test.ts diff --git a/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts b/packages/ai/openai/test/OpenAiLanguageModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts rename to packages/ai/openai/test/OpenAiLanguageModel.test.ts diff --git a/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts b/packages/ai/openai/test/OpenAiSchema.test.ts similarity index 100% rename from repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts rename to packages/ai/openai/test/OpenAiSchema.test.ts diff --git a/packages/ai/openai/test/OpenAiTelemetry.test.ts b/packages/ai/openai/test/OpenAiTelemetry.test.ts new file mode 100644 index 000000000..4cbbc4a49 --- /dev/null +++ b/packages/ai/openai/test/OpenAiTelemetry.test.ts @@ -0,0 +1,24 @@ +import { OpenAiTelemetry } from "@effect/ai-openai" +import { assert, describe, it } from "@effect/vitest" +import { Effect } from "effect" + +describe("OpenAiTelemetry", () => { + it.effect("writes current OpenAI GenAI attribute keys", () => + Effect.gen(function*() { + const span = yield* Effect.currentSpan + OpenAiTelemetry.addGenAIAnnotations(span, { + provider: { name: "openai" }, + openai: { + request: { serviceTier: "auto" }, + response: { serviceTier: "default", systemFingerprint: "fp_1" } + } + }) + const attributes = Object.fromEntries(span.attributes) + assert.deepStrictEqual(attributes, { + "gen_ai.provider.name": "openai", + "openai.request.service_tier": "auto", + "openai.response.service_tier": "default", + "openai.response.system_fingerprint": "fp_1" + }) + }).pipe(Effect.withSpan("test"))) +}) diff --git a/repos/effect/packages/ai/openai/tsconfig.json b/packages/ai/openai/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openai/tsconfig.json rename to packages/ai/openai/tsconfig.json diff --git a/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts b/packages/ai/openai/typetest/OpenAiTool.tst.ts similarity index 100% rename from repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts rename to packages/ai/openai/typetest/OpenAiTool.tst.ts diff --git a/repos/effect/packages/ai/openrouter/CHANGELOG.md b/packages/ai/openrouter/CHANGELOG.md similarity index 94% rename from repos/effect/packages/ai/openrouter/CHANGELOG.md rename to packages/ai/openrouter/CHANGELOG.md index d6447781b..36088fdc2 100644 --- a/repos/effect/packages/ai/openrouter/CHANGELOG.md +++ b/packages/ai/openrouter/CHANGELOG.md @@ -1,5 +1,22 @@ # @effect/ai-openrouter +## 4.0.2 + +### Patch Changes + +- [#8870](https://github.com/Effect-TS/effect/pull/8870) [`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1) Thanks @Makisuo! - Update AI tracing to use current OpenTelemetry GenAI conventions: + + - Replace `gen_ai.system` with `gen_ai.provider.name`. The `system` option, `BaseAttributes` and `WellKnownSystem` are removed in favor of `provider: { name }`, `ProviderAttributes` and `WellKnownProviderName`. + - Remove `gen_ai.token.type`, which is not a span attribute in the current conventions, along with the `token` option, `TokenAttributes` and the `AllAttributes` types. + - `LanguageModel` spans are client spans and record `gen_ai.output.type` (`text` or `json`) for every provider. The OpenAI `responseFormat` telemetry option and `WellKnownResponseFormat` are removed. + - Move OpenAI service-tier and system-fingerprint attributes from `gen_ai.openai.*` to `openai.*`. + - Rename `concurrency`, `toolChoice` and `objectName` span attributes to `effect.ai.concurrency`, `effect.ai.tool_choice` and `effect.ai.object_name`. Tool choices record the resolved value, defaulting to `"auto"`; object choices are JSON-encoded. + - Include cached tokens in Anthropic `gen_ai.usage.input_tokens` and report cache reads and writes as `gen_ai.usage.cache_read.input_tokens` and `gen_ai.usage.cache_write.input_tokens`. The duplicate Anthropic `cacheCreationInputTokens` and `cacheReadInputTokens` response options are removed. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/ai/openrouter/README.md b/packages/ai/openrouter/README.md similarity index 100% rename from repos/effect/packages/ai/openrouter/README.md rename to packages/ai/openrouter/README.md diff --git a/repos/effect/packages/ai/openrouter/codegen.yml b/packages/ai/openrouter/codegen.yml similarity index 100% rename from repos/effect/packages/ai/openrouter/codegen.yml rename to packages/ai/openrouter/codegen.yml diff --git a/repos/effect/packages/ai/openrouter/package.json b/packages/ai/openrouter/package.json similarity index 98% rename from repos/effect/packages/ai/openrouter/package.json rename to packages/ai/openrouter/package.json index db373fd35..46b99545b 100644 --- a/repos/effect/packages/ai/openrouter/package.json +++ b/packages/ai/openrouter/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openrouter", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "An OpenRouter provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/openrouter/src/Generated.ts b/packages/ai/openrouter/src/Generated.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/Generated.ts rename to packages/ai/openrouter/src/Generated.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts b/packages/ai/openrouter/src/OpenRouterClient.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts rename to packages/ai/openrouter/src/OpenRouterClient.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts b/packages/ai/openrouter/src/OpenRouterConfig.ts similarity index 96% rename from repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts rename to packages/ai/openrouter/src/OpenRouterConfig.ts index 1939acd31..50db2fde2 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts +++ b/packages/ai/openrouter/src/OpenRouterConfig.ts @@ -1,6 +1,7 @@ /** * Scoped HTTP client customization for OpenRouter requests. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -13,6 +14,7 @@ import type { HttpClient } from "effect/http/HttpClient" * * @see {@link withClientTransform} for scoping an HTTP client transformation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -34,6 +36,7 @@ export class OpenRouterConfig extends Context.Service< /** * Types associated with the `OpenRouterConfig` context service. * + * @stability unstable * @since 4.0.0 */ export declare namespace OpenRouterConfig { @@ -53,6 +56,7 @@ export declare namespace OpenRouterConfig { * made by the supplied effect. Streaming chat completions ignore this transform. * Replaces any existing scoped transform; compose them manually to apply both. * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterDecisionModel.ts b/packages/ai/openrouter/src/OpenRouterDecisionModel.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterDecisionModel.ts rename to packages/ai/openrouter/src/OpenRouterDecisionModel.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts b/packages/ai/openrouter/src/OpenRouterError.ts similarity index 98% rename from repos/effect/packages/ai/openrouter/src/OpenRouterError.ts rename to packages/ai/openrouter/src/OpenRouterError.ts index e9ee93af4..9d9c04a62 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts +++ b/packages/ai/openrouter/src/OpenRouterError.ts @@ -4,12 +4,14 @@ * Provides OpenRouter-specific metadata fields for AI error types through * module augmentation, enabling typed access to OpenRouter error details. * + * @stability unstable * @since 4.0.0 */ /** * OpenRouter-specific error metadata fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -31,6 +33,7 @@ export type OpenRouterErrorMetadata = { /** * OpenRouter-specific rate limit metadata fields. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts b/packages/ai/openrouter/src/OpenRouterLanguageModel.ts similarity index 99% rename from repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts rename to packages/ai/openrouter/src/OpenRouterLanguageModel.ts index bf96042f6..0952a3504 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts +++ b/packages/ai/openrouter/src/OpenRouterLanguageModel.ts @@ -1669,7 +1669,7 @@ const annotateRequest = ( request: typeof Generated.ChatRequest.Encoded ): void => { addGenAIAnnotations(span, { - system: "openrouter", + provider: { name: "openrouter" }, operation: { name: "chat" }, request: { model: request.model, diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts b/packages/ai/openrouter/src/OpenRouterSchema.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts rename to packages/ai/openrouter/src/OpenRouterSchema.ts diff --git a/repos/effect/packages/ai/openrouter/src/index.ts b/packages/ai/openrouter/src/index.ts similarity index 91% rename from repos/effect/packages/ai/openrouter/src/index.ts rename to packages/ai/openrouter/src/index.ts index 73678854c..f4b91c02b 100644 --- a/repos/effect/packages/ai/openrouter/src/index.ts +++ b/packages/ai/openrouter/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -17,6 +18,7 @@ export * as Generated from "./Generated.ts" export * as OpenRouterClient from "./OpenRouterClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenRouterConfig from "./OpenRouterConfig.ts" @@ -28,6 +30,7 @@ export * as OpenRouterConfig from "./OpenRouterConfig.ts" export * as OpenRouterDecisionModel from "./OpenRouterDecisionModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenRouterError from "./OpenRouterError.ts" diff --git a/repos/effect/packages/ai/openrouter/src/internal/errors.ts b/packages/ai/openrouter/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/internal/errors.ts rename to packages/ai/openrouter/src/internal/errors.ts diff --git a/repos/effect/packages/ai/openrouter/src/internal/utilities.ts b/packages/ai/openrouter/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/internal/utilities.ts rename to packages/ai/openrouter/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openrouter/test/Generated.test.ts b/packages/ai/openrouter/test/Generated.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/Generated.test.ts rename to packages/ai/openrouter/test/Generated.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts b/packages/ai/openrouter/test/OpenRouterClient.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts rename to packages/ai/openrouter/test/OpenRouterClient.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts b/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts rename to packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts b/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts rename to packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts b/packages/ai/openrouter/test/OpenRouterSchema.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts rename to packages/ai/openrouter/test/OpenRouterSchema.test.ts diff --git a/repos/effect/packages/ai/openrouter/tsconfig.json b/packages/ai/openrouter/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openrouter/tsconfig.json rename to packages/ai/openrouter/tsconfig.json diff --git a/repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts b/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts rename to packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts diff --git a/repos/effect/packages/ai/typesafe/CHANGELOG.md b/packages/ai/typesafe/CHANGELOG.md similarity index 75% rename from repos/effect/packages/ai/typesafe/CHANGELOG.md rename to packages/ai/typesafe/CHANGELOG.md index 018f63668..a7bb9b5da 100644 --- a/repos/effect/packages/ai/typesafe/CHANGELOG.md +++ b/packages/ai/typesafe/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/ai-typesafe +## 4.0.2 + +### Patch Changes + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/ai/typesafe/README.md b/packages/ai/typesafe/README.md similarity index 100% rename from repos/effect/packages/ai/typesafe/README.md rename to packages/ai/typesafe/README.md diff --git a/repos/effect/packages/ai/typesafe/package.json b/packages/ai/typesafe/package.json similarity index 98% rename from repos/effect/packages/ai/typesafe/package.json rename to packages/ai/typesafe/package.json index bf45aa03a..28f861cd0 100644 --- a/repos/effect/packages/ai/typesafe/package.json +++ b/packages/ai/typesafe/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-typesafe", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A TypeSafe DecisionModel provider for Effect", diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts b/packages/ai/typesafe/src/TypeSafeClient.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts rename to packages/ai/typesafe/src/TypeSafeClient.ts diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts b/packages/ai/typesafe/src/TypeSafeConfig.ts similarity index 95% rename from repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts rename to packages/ai/typesafe/src/TypeSafeConfig.ts index 851216a17..f8efceb0f 100644 --- a/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts +++ b/packages/ai/typesafe/src/TypeSafeConfig.ts @@ -1,6 +1,7 @@ /** * Scoped HTTP client customization for TypeSafe requests. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -13,6 +14,7 @@ import type { HttpClient } from "effect/http/HttpClient" * * @see {@link withClientTransform} for scoping an HTTP client transformation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -34,6 +36,7 @@ export class TypeSafeConfig extends Context.Service< /** * Types used by the `TypeSafeConfig` context service. * + * @stability unstable * @since 4.0.0 */ export declare namespace TypeSafeConfig { @@ -52,6 +55,7 @@ export declare namespace TypeSafeConfig { * Transforms the TypeSafe HTTP client for requests made by the supplied effect. * Replaces any existing scoped transform; compose them manually to apply both. * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts b/packages/ai/typesafe/src/TypeSafeDecisionModel.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts rename to packages/ai/typesafe/src/TypeSafeDecisionModel.ts diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts b/packages/ai/typesafe/src/TypeSafeSchema.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts rename to packages/ai/typesafe/src/TypeSafeSchema.ts diff --git a/repos/effect/packages/ai/typesafe/src/index.ts b/packages/ai/typesafe/src/index.ts similarity index 90% rename from repos/effect/packages/ai/typesafe/src/index.ts rename to packages/ai/typesafe/src/index.ts index 872b30078..398dfa405 100644 --- a/repos/effect/packages/ai/typesafe/src/index.ts +++ b/packages/ai/typesafe/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,6 +12,7 @@ export * as TypeSafeClient from "./TypeSafeClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as TypeSafeConfig from "./TypeSafeConfig.ts" diff --git a/repos/effect/packages/ai/typesafe/src/internal/errors.ts b/packages/ai/typesafe/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/internal/errors.ts rename to packages/ai/typesafe/src/internal/errors.ts diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts b/packages/ai/typesafe/test/TypeSafeClient.test.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts rename to packages/ai/typesafe/test/TypeSafeClient.test.ts diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts b/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts rename to packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts b/packages/ai/typesafe/test/TypeSafeSchema.test.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts rename to packages/ai/typesafe/test/TypeSafeSchema.test.ts diff --git a/repos/effect/packages/ai/typesafe/tsconfig.json b/packages/ai/typesafe/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/typesafe/tsconfig.json rename to packages/ai/typesafe/tsconfig.json diff --git a/repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts b/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts rename to packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts diff --git a/repos/effect/packages/atom/react/CHANGELOG.md b/packages/atom/react/CHANGELOG.md similarity index 94% rename from repos/effect/packages/atom/react/CHANGELOG.md rename to packages/atom/react/CHANGELOG.md index a669be84e..d767fc56e 100644 --- a/repos/effect/packages/atom/react/CHANGELOG.md +++ b/packages/atom/react/CHANGELOG.md @@ -1,5 +1,15 @@ # @effect/atom-react +## 4.0.2 + +### Patch Changes + +- [#8841](https://github.com/Effect-TS/effect/pull/8841) [`053e3ea`](https://github.com/Effect-TS/effect/commit/053e3ead5a825cc80f070da1f6f969ed13c703ae) Thanks @tim-smart! - Fix hydration mismatches in delayed Suspense boundaries by sharing a stable server snapshot across atom readers, including `useAtomValue` selectors. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/atom/react/LICENSE b/packages/atom/react/LICENSE similarity index 100% rename from repos/effect/packages/atom/react/LICENSE rename to packages/atom/react/LICENSE diff --git a/repos/effect/packages/atom/react/README.md b/packages/atom/react/README.md similarity index 100% rename from repos/effect/packages/atom/react/README.md rename to packages/atom/react/README.md diff --git a/repos/effect/packages/atom/react/package.json b/packages/atom/react/package.json similarity index 98% rename from repos/effect/packages/atom/react/package.json rename to packages/atom/react/package.json index ab851da10..33646b6cc 100644 --- a/repos/effect/packages/atom/react/package.json +++ b/packages/atom/react/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-react", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "React bindings for the Effect Atom modules", diff --git a/repos/effect/packages/atom/react/src/Hooks.ts b/packages/atom/react/src/Hooks.ts similarity index 87% rename from repos/effect/packages/atom/react/src/Hooks.ts rename to packages/atom/react/src/Hooks.ts index dc10f753e..87b753565 100644 --- a/repos/effect/packages/atom/react/src/Hooks.ts +++ b/packages/atom/react/src/Hooks.ts @@ -4,6 +4,7 @@ * `AsyncResult` atoms with React Suspense, and expose helpers for reading and * deriving `AtomRef` values. * + * @stability unstable * @since 4.0.0 */ "use client" @@ -36,15 +37,27 @@ function makeStore(registry: AtomRegistry.AtomRegistry, atom: Atom.Atom): if (store !== undefined) { return store } + // Keep the hydration snapshot while any reader is subscribed, so late + // boundaries match the server HTML. SSR has no subscribers and reads fresh values. + let subscribers = 0 + let serverSnapshot: { readonly value: A } | undefined const newStore: AtomStore = { subscribe(f) { - return registry.subscribe(atom, f) + subscribers++ + const unsubscribe = registry.subscribe(atom, f) + return () => { + subscribers-- + unsubscribe() + } }, snapshot() { return registry.get(atom) }, getServerSnapshot() { - return Atom.getServerValue(atom, registry) + if (subscribers === 0 || serverSnapshot === undefined) { + serverSnapshot = { value: Atom.getServerValue(atom, registry) } + } + return serverSnapshot.value } } stores.set(atom, newStore) @@ -57,6 +70,26 @@ function useStore(registry: AtomRegistry.AtomRegistry, atom: Atom.Atom): A return React.useSyncExternalStore(store.subscribe, store.snapshot, store.getServerSnapshot) } +function useSelectedStore(registry: AtomRegistry.AtomRegistry, atom: Atom.Atom, f: (_: A) => B): B { + const store = makeStore(registry, atom) + // Both snapshots must return the same selected value for an unchanged source. + const select = React.useMemo(() => { + let last: { readonly source: A; readonly value: B } | undefined + return (source: A): B => { + if (last === undefined || !Object.is(last.source, source)) { + last = { source, value: f(source) } + } + return last.value + } + }, [f]) + + return React.useSyncExternalStore( + store.subscribe, + () => select(store.snapshot()), + () => select(store.getServerSnapshot()) + ) +} + const initialValuesSet = new WeakMap>>() /** @@ -72,6 +105,7 @@ const initialValuesSet = new WeakMap(atom: Atom.Atom, f?: (_: A) => A): A => { const registry = React.useContext(RegistryContext) if (f) { - const atomB = React.useMemo(() => Atom.map(atom, f), [atom, f]) - return useStore(registry, atomB) + return useSelectedStore(registry, atom, f) } return useStore(registry, atom) } @@ -179,6 +221,7 @@ const flattenExit = (exit: Exit.Exit): A => { * @see {@link useAtomSet} for mounting a writable atom while returning a setter * @see {@link useAtomRefresh} for mounting an atom while returning a refresh callback * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -203,6 +246,7 @@ export const useAtomMount = (atom: Atom.Atom): void => { * * @see {@link useAtom} for reading and updating the same writable atom * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -244,6 +288,7 @@ export const useAtomSet = < * * @see {@link useAtomMount} for mounting an atom without returning a refresh callback * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -267,6 +312,7 @@ export const useAtomRefresh = (atom: Atom.Atom): () => void => { * @see {@link useAtomValue} for subscribing to an atom without a setter * @see {@link useAtomSet} for updating a writable atom without subscribing to its value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -365,6 +411,7 @@ function atomResultOrSuspend( * * @see {@link useAtomValue} for reading the raw `AsyncResult` value without Suspense * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -400,6 +447,7 @@ export const useAtomSuspense = ( * @see {@link useAtomValue} for reading an `Atom` from the current registry * @see {@link useAtomRefPropValue} for reading a property ref value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -456,6 +505,7 @@ export const useAtomRef = (ref: AtomRef.ReadonlyRef): A => { * @see {@link useAtomRef} for subscribing to an atom ref value * @see {@link useAtomRefPropValue} for subscribing directly to a property value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -480,6 +530,7 @@ export const useAtomRefProp = (ref: AtomRef.AtomRef, pr * @see {@link useAtomRefProp} for returning the property ref directly * @see {@link useAtomRef} for subscribing to a whole atom ref value * + * @stability unstable * @category hooks * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/src/ReactHydration.ts b/packages/atom/react/src/ReactHydration.ts similarity index 98% rename from repos/effect/packages/atom/react/src/ReactHydration.ts rename to packages/atom/react/src/ReactHydration.ts index 62625d715..bbc8d91c5 100644 --- a/repos/effect/packages/atom/react/src/ReactHydration.ts +++ b/packages/atom/react/src/ReactHydration.ts @@ -5,6 +5,7 @@ * existing Atom values until after commit so React transitions do not update * the current UI too early. * + * @stability unstable * @since 4.0.0 */ "use client" @@ -16,6 +17,7 @@ import { RegistryContext } from "./RegistryContext.ts" * Props for a boundary that applies dehydrated Atom values to the nearest * {@link RegistryContext} while rendering its children. * + * @stability unstable * @category components * @since 4.0.0 */ @@ -42,6 +44,7 @@ export interface HydrationBoundaryProps { * @see {@link Hydration.dehydrate} for producing dehydrated Atom state * @see {@link Hydration.hydrate} for lower-level non-React hydration * + * @stability unstable * @category components * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/src/RegistryContext.ts b/packages/atom/react/src/RegistryContext.ts similarity index 98% rename from repos/effect/packages/atom/react/src/RegistryContext.ts rename to packages/atom/react/src/RegistryContext.ts index 7ea69b455..3509dccff 100644 --- a/repos/effect/packages/atom/react/src/RegistryContext.ts +++ b/packages/atom/react/src/RegistryContext.ts @@ -4,6 +4,7 @@ * atoms. Sharing one registry through React context lets components in the same * subtree read and write the same atom state. * + * @stability unstable * @since 4.0.0 */ "use client" @@ -39,6 +40,7 @@ export function scheduleTask(f: () => void): () => void { * * @see {@link RegistryProvider} for creating and providing a registry for a React subtree * + * @stability unstable * @category context * @since 4.0.0 */ @@ -70,6 +72,7 @@ export const RegistryContext = React.createContext(At * * @see {@link RegistryContext} for the React context supplied by this provider * + * @stability unstable * @category context * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/src/ScopedAtom.ts b/packages/atom/react/src/ScopedAtom.ts similarity index 97% rename from repos/effect/packages/atom/react/src/ScopedAtom.ts rename to packages/atom/react/src/ScopedAtom.ts index 23ab2319f..ea04bc9c8 100644 --- a/repos/effect/packages/atom/react/src/ScopedAtom.ts +++ b/packages/atom/react/src/ScopedAtom.ts @@ -4,6 +4,7 @@ * accessor. Each provider creates its own Atom once, so different subtrees can * use the same scoped atom definition without sharing state. * + * @stability unstable * @since 4.0.0 */ "use client" @@ -19,6 +20,7 @@ import * as React from "react" * Used as the computed property key and marker value stored on `ScopedAtom` * objects. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -32,6 +34,7 @@ export type TypeId = "~@effect/atom-react/ScopedAtom" * Used as the computed property key and marker value stored on `ScopedAtom` * objects. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -63,6 +66,7 @@ export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom" * renderToStaticMarkup(React.createElement(App)) // => "
0
" * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -120,6 +124,7 @@ export interface ScopedAtom
, Input = never> { * renderToStaticMarkup(React.createElement(App)) // => "Ada" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/src/index.ts b/packages/atom/react/src/index.ts similarity index 91% rename from repos/effect/packages/atom/react/src/index.ts rename to packages/atom/react/src/index.ts index 554a0df30..c4350d254 100644 --- a/repos/effect/packages/atom/react/src/index.ts +++ b/packages/atom/react/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/test/index.test.tsx b/packages/atom/react/test/index.test.tsx similarity index 84% rename from repos/effect/packages/atom/react/test/index.test.tsx rename to packages/atom/react/test/index.test.tsx index 6a9e7ecf7..aae21c613 100644 --- a/repos/effect/packages/atom/react/test/index.test.tsx +++ b/packages/atom/react/test/index.test.tsx @@ -9,6 +9,7 @@ import * as Hydration from "effect/reactivity/Hydration" import * as Schema from "effect/Schema" import * as React from "react" import { Suspense } from "react" +import * as ReactDOMClient from "react-dom/client" import { renderToString } from "react-dom/server" import { ErrorBoundary } from "react-error-boundary" import { beforeEach, describe, expect, it, test, vi } from "vitest" @@ -688,6 +689,129 @@ describe("atom-react", { concurrent: false }, () => { expect(getCount).toHaveBeenCalled() expect(screen.getByText("0")).toBeInTheDocument() }) + + it("hydrates a delayed Suspense boundary after the atom changes", async () => { + const userAtom = Atom.make("loading") + + // Reading through a selector also covers the selector's server snapshot. + // It is inline and returns a new object, so its result must be memoized. + function Name({ id }: { id: string }) { + const user = useAtomValue(userAtom, (name) => ({ name: name.toUpperCase() })) + return {user.name} + } + + const Passthrough = ({ children }: { children?: React.ReactNode }) => <>{children} + + function App({ Panel }: { Panel: React.ComponentType<{ children?: React.ReactNode }> }) { + return ( +
+ + ...}> + + + + +
+ ) + } + + const container = document.createElement("div") + container.innerHTML = renderToString( + + + + ) + document.body.append(container) + + // The panel stays dehydrated until its lazy chunk loads. + let loadPanel!: (mod: { default: typeof Passthrough }) => void + const LazyPanel = React.lazy(() => + new Promise<{ default: typeof Passthrough }>((resolve) => { + loadPanel = resolve + }) + ) + const recoverableErrors: Array = [] + + let root!: ReactDOMClient.Root + await act(async () => { + root = ReactDOMClient.hydrateRoot( + container, + + + , + { onRecoverableError: (error) => recoverableErrors.push(error) } + ) + }) + await act(async () => { + registry.set(userAtom, "Alice") + }) + await act(async () => { + loadPanel({ default: Passthrough }) + }) + + expect(recoverableErrors).toEqual([]) + expect(container.innerHTML).toBe( + `
ALICEALICE
` + ) + + act(() => root.unmount()) + container.remove() + }) + + it("keeps the hydrated value until the last reader unmounts", async () => { + const userAtom = Atom.make("loading").pipe(Atom.keepAlive) + + function Name() { + return {useAtomValue(userAtom)} + } + + const app = ( + + + + ) + const html = renderToString(app) + const containers = [0, 1, 2].map(() => { + const container = document.createElement("div") + container.innerHTML = html + document.body.append(container) + return container + }) + const recoverableErrors: Array = [] + const hydrate = (container: HTMLElement) => + ReactDOMClient.hydrateRoot(container, app, { + onRecoverableError: (error) => recoverableErrors.push(error) + }) + + let first!: ReactDOMClient.Root + let second!: ReactDOMClient.Root + await act(async () => { + first = hydrate(containers[0]) + second = hydrate(containers[1]) + }) + await act(async () => { + registry.set(userAtom, "Alice") + }) + + // The second reader keeps the snapshot locked after the first unmounts. + act(() => first.unmount()) + let third!: ReactDOMClient.Root + await act(async () => { + third = hydrate(containers[2]) + }) + + expect(recoverableErrors).toEqual([]) + expect(containers[2].innerHTML).toBe("Alice") + + // Once no reader is subscribed, server renders read the registry again. + act(() => { + second.unmount() + third.unmount() + }) + containers.forEach((container) => container.remove()) + + expect(renderToString(app)).toBe("Alice") + }) }) it("should not execute Atom effects during SSR when using withServerSnapshot", () => { diff --git a/repos/effect/packages/atom/react/tsconfig.json b/packages/atom/react/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/react/tsconfig.json rename to packages/atom/react/tsconfig.json diff --git a/repos/effect/packages/atom/react/vitest.setup.ts b/packages/atom/react/vitest.setup.ts similarity index 100% rename from repos/effect/packages/atom/react/vitest.setup.ts rename to packages/atom/react/vitest.setup.ts diff --git a/repos/effect/packages/atom/solid/CHANGELOG.md b/packages/atom/solid/CHANGELOG.md similarity index 94% rename from repos/effect/packages/atom/solid/CHANGELOG.md rename to packages/atom/solid/CHANGELOG.md index 268e97f5c..5b0c6d470 100644 --- a/repos/effect/packages/atom/solid/CHANGELOG.md +++ b/packages/atom/solid/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/atom-solid +## 4.0.2 + +### Patch Changes + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/atom/solid/LICENSE b/packages/atom/solid/LICENSE similarity index 100% rename from repos/effect/packages/atom/solid/LICENSE rename to packages/atom/solid/LICENSE diff --git a/repos/effect/packages/atom/solid/README.md b/packages/atom/solid/README.md similarity index 100% rename from repos/effect/packages/atom/solid/README.md rename to packages/atom/solid/README.md diff --git a/repos/effect/packages/atom/solid/package.json b/packages/atom/solid/package.json similarity index 98% rename from repos/effect/packages/atom/solid/package.json rename to packages/atom/solid/package.json index 17fa6dda2..80949332f 100644 --- a/repos/effect/packages/atom/solid/package.json +++ b/packages/atom/solid/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-solid", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "SolidJS bindings for the Effect Atom modules", diff --git a/repos/effect/packages/atom/solid/src/Hooks.ts b/packages/atom/solid/src/Hooks.ts similarity index 97% rename from repos/effect/packages/atom/solid/src/Hooks.ts rename to packages/atom/solid/src/Hooks.ts index a8763e874..7c0877fa6 100644 --- a/repos/effect/packages/atom/solid/src/Hooks.ts +++ b/packages/atom/solid/src/Hooks.ts @@ -4,6 +4,7 @@ * for cleanup, subscribe callbacks, seed initial values, expose `AsyncResult` * atoms as Solid resources, and read values from `AtomRef` references. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -33,6 +34,7 @@ const initialValuesSet = new WeakMap(exit: Exit.Exit): A => { * @see {@link useAtomSet} for mounting a writable atom while returning a setter * @see {@link useAtomRefresh} for mounting an atom while returning a refresh callback * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -147,6 +151,7 @@ export const useAtomMount =
(atom: () => Atom.Atom): void => { /** * Returns a setter for a writable atom without subscribing to its value. * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -175,6 +180,7 @@ export const useAtomSet = < /** * Mounts an atom and returns a callback that refreshes the current atom. * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -203,6 +209,7 @@ export const useAtomRefresh = (atom: () => Atom.Atom): () => void => { * @see {@link useAtomValue} for subscribing to an atom without a setter * @see {@link useAtomSet} for updating a writable atom without subscribing to its value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -231,6 +238,7 @@ export const useAtom = ( /** * Converts an `AsyncResult` atom into a Solid resource. * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -288,6 +297,7 @@ const constUnresolvedPromise = new Promise(() => {}) * @see {@link useAtomValue} for reading an `Atom` from the current registry * @see {@link useAtomRefPropValue} for reading a property ref value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -322,6 +332,7 @@ export const useAtomRef = (ref: () => AtomRef.ReadonlyRef): Accessor => * @see {@link useAtomRef} for subscribing to an atom ref value * @see {@link useAtomRefPropValue} for subscribing directly to a property value * + * @stability unstable * @category hooks * @since 4.0.0 */ @@ -352,6 +363,7 @@ export const useAtomRefProp = ( * @see {@link useAtomRef} for subscribing to a whole atom ref value * @see {@link useAtomRefProp} for returning the property ref directly * + * @stability unstable * @category hooks * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/solid/src/RegistryContext.ts b/packages/atom/solid/src/RegistryContext.ts similarity index 97% rename from repos/effect/packages/atom/solid/src/RegistryContext.ts rename to packages/atom/solid/src/RegistryContext.ts index b534f26c3..ab327f76f 100644 --- a/repos/effect/packages/atom/solid/src/RegistryContext.ts +++ b/packages/atom/solid/src/RegistryContext.ts @@ -4,6 +4,7 @@ * atoms. Sharing one registry through Solid context lets components and * computations in the same owner tree read and write the same atom state. * + * @stability unstable * @since 4.0.0 */ import type * as Atom from "effect/reactivity/Atom" @@ -26,6 +27,7 @@ import { createComponent, createContext, onCleanup } from "solid-js" * * @see {@link RegistryProvider} for creating and providing a registry for a Solid subtree * + * @stability unstable * @category context * @since 4.0.0 */ @@ -54,6 +56,7 @@ export const RegistryContext = createContext(AtomRegi * * @see {@link RegistryContext} for the context supplied by this provider * + * @stability unstable * @category context * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/solid/src/index.ts b/packages/atom/solid/src/index.ts similarity index 85% rename from repos/effect/packages/atom/solid/src/index.ts rename to packages/atom/solid/src/index.ts index e8c448255..e6ce741b7 100644 --- a/repos/effect/packages/atom/solid/src/index.ts +++ b/packages/atom/solid/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/solid/test/index.test.tsx b/packages/atom/solid/test/index.test.tsx similarity index 100% rename from repos/effect/packages/atom/solid/test/index.test.tsx rename to packages/atom/solid/test/index.test.tsx diff --git a/repos/effect/packages/atom/solid/tsconfig.json b/packages/atom/solid/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/solid/tsconfig.json rename to packages/atom/solid/tsconfig.json diff --git a/repos/effect/packages/atom/solid/vitest.setup.ts b/packages/atom/solid/vitest.setup.ts similarity index 100% rename from repos/effect/packages/atom/solid/vitest.setup.ts rename to packages/atom/solid/vitest.setup.ts diff --git a/repos/effect/packages/atom/vue/CHANGELOG.md b/packages/atom/vue/CHANGELOG.md similarity index 94% rename from repos/effect/packages/atom/vue/CHANGELOG.md rename to packages/atom/vue/CHANGELOG.md index 9136e5241..14c477bcf 100644 --- a/repos/effect/packages/atom/vue/CHANGELOG.md +++ b/packages/atom/vue/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/atom-vue +## 4.0.2 + +### Patch Changes + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/atom/vue/LICENSE b/packages/atom/vue/LICENSE similarity index 100% rename from repos/effect/packages/atom/vue/LICENSE rename to packages/atom/vue/LICENSE diff --git a/repos/effect/packages/atom/vue/README.md b/packages/atom/vue/README.md similarity index 100% rename from repos/effect/packages/atom/vue/README.md rename to packages/atom/vue/README.md diff --git a/repos/effect/packages/atom/vue/package.json b/packages/atom/vue/package.json similarity index 98% rename from repos/effect/packages/atom/vue/package.json rename to packages/atom/vue/package.json index 414356bd0..6d7ba713a 100644 --- a/repos/effect/packages/atom/vue/package.json +++ b/packages/atom/vue/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-vue", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "Vue bindings for the Effect Atom modules", diff --git a/repos/effect/packages/atom/vue/src/index.ts b/packages/atom/vue/src/index.ts similarity index 86% rename from repos/effect/packages/atom/vue/src/index.ts rename to packages/atom/vue/src/index.ts index ecd8a09c7..39e0a210f 100644 --- a/repos/effect/packages/atom/vue/src/index.ts +++ b/packages/atom/vue/src/index.ts @@ -1,4 +1,7 @@ /** + * Vue composables for reading and writing Effect atoms with an injected registry. + * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -11,62 +14,83 @@ import * as AtomRegistry from "effect/reactivity/AtomRegistry" import { computed, type ComputedRef, inject, type InjectionKey, type Ref, shallowRef, watchEffect } from "vue" /** + * Re-exports the atom registry API. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as AtomRegistry from "effect/reactivity/AtomRegistry" /** + * Re-exports the asynchronous atom result API. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as AsyncResult from "effect/reactivity/AsyncResult" /** + * Re-exports the atom API. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as Atom from "effect/reactivity/Atom" /** + * Re-exports the atom reference API. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as AtomRef from "effect/reactivity/AtomRef" /** + * Re-exports the HTTP API atom helpers. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as AtomHttpApi from "effect/reactivity/AtomHttpApi" /** + * Re-exports the RPC atom helpers. + * * @stability unstable - * @since 4.0.0 * @category re-exports + * @since 4.0.0 */ export * as AtomRpc from "effect/reactivity/AtomRpc" /** - * @since 4.0.0 + * Vue injection key for an atom registry. + * + * @stability unstable * @category symbols + * @since 4.0.0 */ export const registryKey = Symbol.for("@effect/atom-vue/registryKey") as InjectionKey /** - * @since 4.0.0 + * Fallback atom registry used when no registry is provided. + * + * @stability unstable * @category constants + * @since 4.0.0 */ export const defaultRegistry: AtomRegistry.AtomRegistry = AtomRegistry.make() /** - * @since 4.0.0 + * Returns the injected atom registry, falling back to the default registry. + * + * @stability unstable * @category accessors + * @since 4.0.0 */ export const injectRegistry = (): AtomRegistry.AtomRegistry => { return inject(registryKey, defaultRegistry) @@ -85,8 +109,11 @@ const useAtomValueRef = >(atom: () => A) => { } /** - * @since 4.0.0 + * Returns a reactive atom value and a setter with the requested write mode. + * + * @stability unstable * @category composables + * @since 4.0.0 */ export const useAtom = ( atom: () => Atom.Writable, @@ -108,8 +135,11 @@ export const useAtom = (atom: () => Atom.Atom): Readonly> => useAtomValueRef(atom)[0] @@ -162,8 +192,11 @@ function setAtom } /** - * @since 4.0.0 + * Returns a setter for an atom, mounting it for the lifetime of the composable. + * + * @stability unstable * @category composables + * @since 4.0.0 */ export const useAtomSet = < R, @@ -201,8 +234,11 @@ export const useAtomSet = < } /** - * @since 4.0.0 + * Returns a read-only Vue ref that tracks an atom reference. + * + * @stability unstable * @category composables + * @since 4.0.0 */ export const useAtomRef = (atomRef: () => AtomRef.ReadonlyRef): Readonly> => { const atomRefRef = computed(atomRef) diff --git a/repos/effect/packages/atom/vue/test/index.test.ts b/packages/atom/vue/test/index.test.ts similarity index 100% rename from repos/effect/packages/atom/vue/test/index.test.ts rename to packages/atom/vue/test/index.test.ts diff --git a/repos/effect/packages/atom/vue/tsconfig.json b/packages/atom/vue/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/vue/tsconfig.json rename to packages/atom/vue/tsconfig.json diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/cli/.madgerc b/packages/cli/.madgerc deleted file mode 100644 index b407c6b4b..000000000 --- a/packages/cli/.madgerc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "detectiveOptions": { - "ts": { - "skipTypeImports": true - } - } -} diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore deleted file mode 100644 index e4c612bb4..000000000 --- a/packages/cli/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -*.test.js -*.test.d.ts -tsconfig.build.json -*.log -tslint.json -.eslintrc.js -.eslintignore -.madgerc -.swcrc -.cache -coverage/ -.tsbuildinfo -.build/ -!dist/ diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md deleted file mode 100644 index 7353fe215..000000000 --- a/packages/cli/CHANGELOG.md +++ /dev/null @@ -1,1657 +0,0 @@ -# @effect-app/cli - -## 2.1.0-beta.44 - -### Patch Changes - -- 6b57330: Upgrade Effect packages to npm `4.0.0`. - -## 2.1.0-beta.43 - -### Patch Changes - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. - -## 2.1.0-beta.42 - -### Patch Changes - -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -## 2.1.0-beta.41 - -### Patch Changes - -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -## 2.1.0-beta.40 - -### Patch Changes - -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -## 2.1.0-beta.39 - -### Patch Changes - -- 68f1b39: Make `effa ue` update Effect and Effect App packages to the highest versions allowed by their existing semver ranges, including compatible prerelease updates. - -## 2.1.0-beta.38 - -### Patch Changes - -- e2c9ebd: Require an explicit `EFFA_GIST_CACHE_ID`, remove cache Gist discovery and automatic creation, and initialize missing company pointer files once. - -## 2.1.0-beta.37 - -### Patch Changes - -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.36 - -### Patch Changes - -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.35 - -### Patch Changes - -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.34 - -### Patch Changes - -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. - -## 2.1.0-beta.33 - -### Patch Changes - -- 32f05c8: Remove unused dependency declarations from package manifests. - -## 2.1.0-beta.32 - -### Patch Changes - -- b9312df: Prevent subtree sync commands from importing upstream git tags. - -## 2.1.0-beta.31 - -### Patch Changes - -- e7ad78c: Add `sync-effect-app` to sync vendored Effect App libs by pinned package version. - The command prefers package-version git tags and supports `--ref latest` for main. -- e7ad78c: Sync Effect subtrees from a repository URL without requiring a local git remote. - -## 2.1.0-beta.30 - -### Patch Changes - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). - -## 2.1.0-beta.29 - -### Patch Changes - -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -## 2.1.0-beta.28 - -### Patch Changes - -- 55c6572: update packages - -## 2.1.0-beta.27 - -### Patch Changes - -- 7b28548: Add `effa sync --update [--ref ]` to bump the pinned `ref` in `.shared.json` to the latest sha before syncing. Without `--ref` it resolves the shared repo's default branch HEAD; with `--ref` it resolves that ref (branches resolved against `origin/` so you get the latest remote commit). The resolved sha is written back to the lockfile. Omitting `--update` keeps the existing pin-and-sync behaviour. - -## 2.1.0-beta.26 - -### Patch Changes - -- bogus - -## 2.1.0-beta.25 - -### Patch Changes - -- 6138bc7: `effa sync-push`: propagate locally-deleted synced files. Previously, files removed from the consuming project were silently skipped (the loop bailed when `destAbs` didn't exist). Now they're recorded as deletions, `git rm`'d in the cache branch, and listed in the PR body alongside modifications. - -## 2.1.0-beta.24 - -### Minor Changes - -- 07dcd75: Add `effa sync` / `effa sync-diff` / `effa sync-push` subcommands for syncing content (architecture docs, e2e helpers, ts-plugins) from `effect-app/shared` into consuming projects per a project-side `.shared.json` lockfile. - - - `effa sync` — clone/checkout the shared repo at the pinned ref into `~/.cache/effa/shared/`, then copy the artifact map's files into the project (honoring `exclude`). - - `effa sync-diff` — sha256 compare each tracked file against the cache copy; reports `M` (modified locally), `D` (missing from project), `E` (excluded). - - `effa sync-push [--pr] [-m msg] [--branch name]` — branch in the cache off the pinned ref, copy modified project files in, commit, push. Optional `--pr` opens a PR via `gh pr create`. - - Lockfile shape: - - ```json - { - "repo": "github.com/effect-app/shared", - "ref": "", - "artifacts": { "": "" }, - "exclude": [""] - } - ``` - -## 2.1.0-beta.23 - -### Patch Changes - -- b8b9080: update packages - -## 2.1.0-beta.22 - -### Patch Changes - -- 6cfd83d: update effect to latest beta - -## 2.1.0-beta.21 - -### Patch Changes - -- c991be1: update packages - -## 2.1.0-beta.20 - -### Patch Changes - -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. - -## 2.1.0-beta.19 - -### Patch Changes - -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -## 2.1.0-beta.18 - -### Patch Changes - -- c1e73de: - -## 2.1.0-beta.17 - -### Patch Changes - -- 8434b5f: preserve trailing newline when writing package.json files - -## 2.1.0-beta.16 - -### Patch Changes - -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. - -## 2.1.0-beta.15 - -### Patch Changes - -- c3299f7: update packages - -## 2.1.0-beta.14 - -### Patch Changes - -- Update effect packages to 4.0.0-beta.52 - -## 2.1.0-beta.13 - -### Minor Changes - -- 97207c0: Add `sync-effect` CLI command to sync the Effect git subtree to the version pinned in package.json manifests. - -## 2.0.1-beta.12 - -### Patch Changes - -- fac725d: update effect to latest beta - -## 2.0.1-beta.11 - -### Patch Changes - -- 8267e7d: update to latest effect - -## 2.0.1-beta.10 - -### Patch Changes - -- 1f9d7da: fix missing toCodecJson usages - -## 2.0.1-beta.9 - -### Patch Changes - -- cec026d: update packages - -## 2.0.1-beta.8 - -### Patch Changes - -- d867272: the return of `Context` - -## 2.0.1-beta.7 - -### Patch Changes - -- 10b55ff: update packages - -## 2.0.1-beta.6 - -### Patch Changes - -- a37aa38: Update to effect beta 43 - -## 2.0.1-beta.5 - -### Patch Changes - -- 8c645d5: update to latest effect - -## 2.0.1-beta.4 - -### Patch Changes - -- 601a1ff: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it - -## 2.0.1-beta.3 - -### Patch Changes - -- 75c72ee: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined - -## 2.0.1-beta.2 - -### Patch Changes - -- 1c3c541: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- de2359d: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- 0d72f34: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. - -## 2.0.1-beta.1 - -### Patch Changes - -- 01c70d0: update all teh tings - -## 2.0.1-beta.0 - -### Patch Changes - -- 64786af: Beta25 - -## 2.0.0 - -### Major Changes - -- 7e9e02b: Update to Effect v4 - -## 1.29.2 - -### Patch Changes - -- 5880020: bootstrap - -## 1.29.1 - -### Patch Changes - -- 5d043d3: propagate exit code - -## 1.29.0 - -### Minor Changes - -- 1a102e2: update packages - -## 1.28.1 - -### Patch Changes - -- 358782e: update packages - -## 1.28.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -## 1.27.0 - -### Minor Changes - -- b8f5e30: update packages - -## 1.26.5 - -### Patch Changes - -- 7da965e: better multi-tenant handling (multi env, different caches) - -## 1.26.4 - -### Patch Changes - -- 36aba87: move gh presence check when logging in because of freakin' services initializations - -## 1.26.3 - -### Patch Changes - -- 25d353c: proper login - -## 1.26.2 - -### Patch Changes - -- 73288cc: move GH check - -## 1.26.1 - -### Patch Changes - -- d1a6901: check gh presence - -## 1.26.0 - -### Minor Changes - -- 2bcc323: add cli gist command to work with github gist files - -## 1.25.0 - -### Minor Changes - -- 6d8c512: update packages - -## 1.24.0 - -### Minor Changes - -- 3d49d81: update packages - -## 1.23.25 - -### Patch Changes - -- e31d54f: remove out of date wiki commands - -## 1.23.24 - -### Patch Changes - -- f35da3c: porting more to eslint shared config package - -## 1.23.23 - -### Patch Changes - -- 8096576: new eslint shared config package - -## 1.23.22 - -### Patch Changes - -- a3ac3bb: fix export (?) - -## 1.23.21 - -### Patch Changes - -- f9aee77: some fixes - -## 1.23.20 - -### Patch Changes - -- f6a3328: export base eslint files so that we import them in projects - -## 1.23.19 - -### Patch Changes - -- 539e2f2: minor wiki command enhancements - -## 1.23.18 - -### Patch Changes - -- 84be367: add command to update all but effect/effect-app packages - -## 1.23.17 - -### Patch Changes - -- d1dd883: other fml fixes - -## 1.23.16 - -### Patch Changes - -- ccbf803: fml - -## 1.23.15 - -### Patch Changes - -- 8994599: fix some wiki commands - -## 1.23.14 - -### Patch Changes - -- 2cad28a: some fixes to wiki commands - -## 1.23.13 - -### Patch Changes - -- d9280c7: fix init wiki - -## 1.23.12 - -### Patch Changes - -- 9ff7964: add wiki-init - -## 1.23.11 - -### Patch Changes - -- 7c2e428: pnpm effa wiki update - -## 1.23.10 - -### Patch Changes - -- 8133014: cli doc always fetch remote to not miss any update (like github wiki interface ones) - -## 1.23.9 - -### Patch Changes - -- 35f8488: fix sync cli command - -## 1.23.8 - -### Patch Changes - -- 5576081: cli command for interacting with wiki (v1) - -## 1.23.7 - -### Patch Changes - -- 8cb893c: dprint rules the world - -## 1.23.6 - -### Patch Changes - -- e81f093: remove watch add nuke - -## 1.23.5 - -### Patch Changes - -- de35a0f: add monitors for packagejson -- 8e1543e: add wrapping of child commands - -## 1.23.4 - -### Patch Changes - -- ca792d7: add some debug prints - -## 1.23.3 - -### Patch Changes - -- 62b36b0: fix monitors blocking next commands by forking them - -## 1.23.2 - -### Patch Changes - -- c12a49b: effect app cli to effect cli - -## 1.23.1 - -### Patch Changes - -- 20293b1: add ncu:effect and ncu:effect-app commands - -## 1.23.0 - -### Minor Changes - -- 0f2b2cf: update packages - -## 1.22.1 - -### Patch Changes - -- b095bb5: tscp - -## 1.22.0 - -### Minor Changes - -- 2c217ce: update packages - -## 1.21.1 - -### Patch Changes - -- b771967: update packages - -## 1.21.0 - -### Minor Changes - -- eac9c67: update packages - -## 1.20.1 - -### Patch Changes - -- 49d93a5: update packages - -## 1.20.0 - -### Minor Changes - -- b1d4af4: housekeeping: update packages - -## 1.19.0 - -### Minor Changes - -- 8cb33d5: update packages - -## 1.18.5 - -### Patch Changes - -- 1e1f32b: housekeeping: update packages - -## 1.18.4 - -### Patch Changes - -- update packages - -## 1.18.3 - -### Patch Changes - -- 0fcab38: update packages - -## 1.18.2 - -### Patch Changes - -- ae71bd9: add missing link for vue-componentsy - -## 1.18.1 - -### Patch Changes - -- fdb1107: filter dir - -## 1.18.0 - -### Minor Changes - -- 14ea804: housekeeping: update packages - -## 1.17.1 - -### Patch Changes - -- 4c8dfb4: update packages - -## 1.17.0 - -### Minor Changes - -- e42d5fb: update to effect 3.14 with native rpc2 - -## 1.16.2 - -### Patch Changes - -- 36996ed: housekeeping: update packages - -## 1.16.1 - -### Patch Changes - -- 63e966a: update packages - -## 1.16.0 - -### Minor Changes - -- 8be2984: housekeeping: update packages - -## 1.15.0 - -### Minor Changes - -- 1721761: improve: only update nearest controllers/routes.ts - -## 1.14.0 - -### Minor Changes - -- 9b2e385: update packages - -## 1.13.2 - -### Patch Changes - -- 9d11dfb: housekeeping: update packages - -## 1.13.1 - -### Patch Changes - -- ab9d1ea: housekeeping: update packages - -## 1.13.0 - -### Minor Changes - -- c8ebfb4: update packages - -## 1.12.0 - -### Minor Changes - -- 67023f8: kill \_cjs - -## 1.11.4 - -### Patch Changes - -- 9f504c6: update packages - -## 1.11.3 - -### Patch Changes - -- 750b46a: update packages - -## 1.11.2 - -### Patch Changes - -- 1bfc3d8: update packages - -## 1.11.1 - -### Patch Changes - -- 5ef6481: update packages - -## 1.11.0 - -### Minor Changes - -- 29a1a65: update packages - -## 1.10.0 - -### Minor Changes - -- d84aef7: update packages - -## 1.9.0 - -### Minor Changes - -- cc2c43e: update packages - -## 1.8.0 - -### Minor Changes - -- bump - -## 1.7.1 - -### Patch Changes - -- c971d73: update packages - -## 1.7.0 - -### Minor Changes - -- update - -## 1.6.0 - -### Minor Changes - -- f19762b: update packages - -## 1.5.1 - -### Patch Changes - -- 31c79d7: exit - -## 1.5.0 - -### Minor Changes - -- 4728346: add 'sync' command - -## 1.4.1 - -### Patch Changes - -- 412cb3d: update packages - -## 1.4.0 - -### Minor Changes - -- 590e5bf: update packages - -## 1.3.0 - -### Minor Changes - -- d4ab73f: update packages - -## 1.2.1 - -### Patch Changes - -- 19ea035: update packages - -## 1.2.0 - -### Minor Changes - -- d5623a6: update packages - -## 1.1.2 - -### Patch Changes - -- f68dbd2: update packages - -## 1.1.1 - -### Patch Changes - -- a66651b: m - -## 1.1.0 - -### Minor Changes - -- 964b0d2: refactor: merge adapters into infra - -## 1.0.0 - -### Major Changes - -- 13abe12: refactor: merge core into prelude - -## 0.90.0 - -### Minor Changes - -- d0471b1: refactor: merge @effect-app/schema into core - -## 0.89.0 - -### Minor Changes - -- 3872061: migrate to effect/Schema - -## 0.88.0 - -### Minor Changes - -- 480f0ef: add new controller setup support - -## 0.87.1 - -### Patch Changes - -- cda1477: update packages - -## 0.87.0 - -### Minor Changes - -- update packages - -## 0.86.0 - -### Minor Changes - -- c56949e: drop fluent extensions - -## 0.85.0 - -### Minor Changes - -- b9b64b8: housekeeping: update packages - -## 0.84.0 - -### Minor Changes - -- 65dcc73: update packages - -## 0.83.0 - -### Minor Changes - -- 4d2b804: housekeeping: update packages - -## 0.82.0 - -### Minor Changes - -- 190b139: update packages - -## 0.81.1 - -### Patch Changes - -- c710953: update packagesy - -## 0.81.0 - -### Minor Changes - -- 1018e58: update packages - -## 0.80.1 - -### Patch Changes - -- 11982eb: fix cli - -## 0.80.0 - -### Minor Changes - -- c5f5223: update packages - -## 0.79.3 - -### Patch Changes - -- e6eecd2: update packages - -## 0.79.2 - -### Patch Changes - -- ff - -## 0.79.1 - -### Patch Changes - -- fix - -## 0.79.0 - -### Minor Changes - -- 2143dd9: update packages - -## 0.78.1 - -### Patch Changes - -- f6066ac: update packages - -## 0.78.0 - -### Minor Changes - -- 79f58af: update packages - -## 0.77.2 - -### Patch Changes - -- b3ebc41: update packages - -## 0.77.1 - -### Patch Changes - -- f569681: update packages - -## 0.77.0 - -### Minor Changes - -- 613c148: update packages - -## 0.76.0 - -### Minor Changes - -- 8d0c511: update packages - -## 0.75.0 - -### Minor Changes - -- 9cc6f86: update packages - -## 0.74.1 - -### Patch Changes - -- 2c661c1: update packages - -## 0.74.0 - -### Minor Changes - -- 9ac9416: update packages - -## 0.73.2 - -### Patch Changes - -- 3411e78: update packages - -## 0.73.1 - -### Patch Changes - -- 128f636: update packages - -## 0.73.0 - -### Minor Changes - -- 2e29520: update packages - -## 0.72.1 - -### Patch Changes - -- c4dbb6c: update packages - -## 0.72.0 - -### Minor Changes - -- 0d1b378: update to latest schema - -## 0.71.0 - -### Minor Changes - -- 290a709: update packages - -## 0.70.0 - -### Minor Changes - -- ecb05f6: update packages - -## 0.69.0 - -### Minor Changes - -- revert form changes - -## 0.68.0 - -### Minor Changes - -- a9a81a1: cleanup ext - -## 0.67.2 - -### Patch Changes - -- fix - -## 0.67.1 - -### Patch Changes - -- 8dc2b62: improve link - -## 0.67.0 - -### Minor Changes - -- 7a3f05c: update packages - -## 0.66.0 - -### Minor Changes - -- bbaa558: bs - -## 0.65.0 - -### Minor Changes - -- e5b0d8e: update packages -- 2e2fa98: update packages - -## 0.64.0 - -### Minor Changes - -- 494842c: update packages - -## 0.63.1 - -### Patch Changes - -- 0334285: update packages - -## 0.63.0 - -### Minor Changes - -- 51253a2: update packages - -## 0.62.0 - -### Minor Changes - -- update packages - -## 0.61.0 - -### Minor Changes - -- d0a1774: update ts to 5.4.1-rc - -## 0.60.0 - -### Minor Changes - -- 7346691: update packages - -## 0.59.0 - -### Minor Changes - -- e8e8ee2: update packages - -## 0.58.0 - -### Minor Changes - -- 117be64: bump - -## 0.57.0 - -### Minor Changes - -- A new day - -## 0.56.0 - -### Minor Changes - -- c070e0d: update effect - -## 0.55.1 - -### Patch Changes - -- b7daa54: improve supported commands prompt - -## 0.55.0 - -### Minor Changes - -- db67c43: update bins - -## 0.54.0 - -### Minor Changes - -- 57fcfef: update packages - -## 0.53.2 - -### Patch Changes - -- 1168eb4: add link/unlink - -## 0.53.1 - -### Patch Changes - -- c550140: fix - -## 0.53.0 - -### Minor Changes - -- 25123dc: improve cli frontend - -## 0.52.0 - -### Minor Changes - -- 6e5c606: cl - -## 0.51.0 - -### Minor Changes - -- de336f6: src - -## 0.50.1 - -### Patch Changes - -- f10e630: update packages - -## 0.50.0 - -### Minor Changes - -- 47f46f0: use actual action name casing so that editor go to works. - -## 0.49.1 - -### Patch Changes - -- 7b40fa7: use full path - -## 0.49.0 - -### Minor Changes - -- m - -## 0.48.0 - -### Minor Changes - -- bs - -## 0.47.0 - -### Minor Changes - -- c8a07bc: fix path - -## 0.46.0 - -### Minor Changes - -- e5cdd42: update packages - -## 0.45.0 - -### Minor Changes - -- update to latest Effect "minor" - -## 0.44.0 - -### Minor Changes - -- c29b6ff: bump - -## 0.43.0 - -### Minor Changes - -- bump - -## 0.42.1 - -### Patch Changes - -- 0a898f8: update packages - -## 0.42.0 - -### Minor Changes - -- 449b343: update packages - -## 0.41.0 - -### Minor Changes - -- 2fd03d3: update packages - -## 0.40.0 - -### Minor Changes - -- a30db88: update packages - -## 0.39.0 - -### Minor Changes - -- 3eb4057: update packages - -## 0.38.0 - -### Minor Changes - -- 4c915ea: update packages - -## 0.37.0 - -### Minor Changes - -- a894a63: update packages - -## 0.36.0 - -### Minor Changes - -- 7945f38: update packages - -## 0.35.0 - -### Minor Changes - -- 63425a7: update packages - -## 0.34.0 - -### Minor Changes - -- 82f0f0d: update packages - -## 0.33.1 - -### Patch Changes - -- cf6ae3c: update packages - -## 0.33.0 - -### Minor Changes - -- 46bd7be: update packages - -## 0.32.0 - -### Minor Changes - -- a681ab6: update packages - -## 0.31.0 - -### Minor Changes - -- a9e8b8f: update packages - -## 0.30.0 - -### Minor Changes - -- align v - -## 0.29.0 - -### Minor Changes - -- align v - -## 0.28.0 - -### Minor Changes - -- 1f6b9a1: update packages - -## 0.27.0 - -### Minor Changes - -- c528c33: initial alignment of Schema with effect/schema - -## 0.26.0 - -### Minor Changes - -- 6c180253: cleanup - -## 0.25.1 - -### Patch Changes - -- 7ff1f6f9: update packages - -## 0.25.0 - -### Minor Changes - -- 0a4bfbc7: update packages - -## 0.24.0 - -### Minor Changes - -- 90af6a41: update packages - -## 0.23.1 - -### Patch Changes - -- aa3c98d1: update packages - -## 0.23.0 - -### Minor Changes - -- update packages - -## 0.22.1 - -### Patch Changes - -- 780a6718: update packages - -## 0.22.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.21.2 - -### Patch Changes - -- 838eeb10: update packages - -## 0.21.1 - -### Patch Changes - -- fix tsplus.config for pnpm - -## 0.21.0 - -### Minor Changes - -- update packages - -## 0.20.0 - -### Minor Changes - -- add match and update packages - -## 0.19.0 - -### Minor Changes - -- update packages - -## 0.18.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.17.0 - -### Minor Changes - -- c80b9e7a: Updated to latest Effect - -## 0.16.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.15.0 - -### Minor Changes - -- Update packages - -## 0.14.0 - -### Minor Changes - -- Update depdendencies - -## 0.13.0 - -### Minor Changes - -- update dependencies - -## 0.12.0 - -### Minor Changes - -- Update dependencies - -## 0.11.0 - -### Minor Changes - -- Update packages - -## 0.10.0 - -### Minor Changes - -- update dependencies - -## 0.9.0 - -### Minor Changes - -- Update packages - -## 0.8.0 - -### Minor Changes - -- Update to latest effect - -## 0.7.0 - -### Minor Changes - -- Update packages - -## 0.6.0 - -### Minor Changes - -- update packages - -## 0.5.0 - -### Minor Changes - -- Update tsplus with companion instance extensions - -## 0.4.0 - -### Minor Changes - -- Update Effect - -## 0.3.1 - -### Patch Changes - -- 02997e8f: FUP - -## 0.3.0 - -### Minor Changes - -- 4d49f303: Upgrade to @effect/data - -## 0.2.7 - -### Patch Changes - -- 82a54d23: Cleanup - -## 0.2.6 - -### Patch Changes - -- 89ee2cc5: Add root index.ts support - -## 0.2.5 - -### Patch Changes - -- 9ae82030: fix index - -## 0.2.4 - -### Patch Changes - -- fe896f8c: Fix m - -## 0.2.3 - -### Patch Changes - -- 5a37df3f: Improve cli - -## 0.2.2 - -### Patch Changes - -- 1cd7bbd9: write package.json at start - -## 0.2.1 - -### Patch Changes - -- 847c1eb0: Improve cli dirs - -## 0.2.0 - -### Minor Changes - -- Update annotations and leverage ForceLazy on Effect \_\_call - -## 0.1.6 - -### Patch Changes - -- 912ba66c: Update packages - -## 0.1.5 - -### Patch Changes - -- 2cc4390c: Fix cli - -## 0.1.4 - -### Patch Changes - -- dccdfe2a: cli: add packagejson levels - -## 0.1.3 - -### Patch Changes - -- 2946e475: Fix - -## 0.1.2 - -### Patch Changes - -- 5fd3e5f5: Add package json exports support - -## 0.1.1 - -### Patch Changes - -- f8d2dea7: Add index commands - -## 0.1.0 - -### Minor Changes - -- 9150d615: Prepare CLI for multi-command - -## 0.0.2 - -### Patch Changes - -- Initial release diff --git a/packages/cli/bin.js b/packages/cli/bin.js deleted file mode 100755 index 9c4ff2202..000000000 --- a/packages/cli/bin.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -import "@effect-app/cli" diff --git a/packages/cli/package.json b/packages/cli/package.json deleted file mode 100644 index a255c9b93..000000000 --- a/packages/cli/package.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "name": "@effect-app/cli", - "version": "2.1.0-beta.44", - "license": "MIT", - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/cli" - }, - "bin": { - "effect-app": "./bin.js", - "effa": "./bin.js", - "effect-app-cli": "./bin.js" - }, - "dependencies": { - "@effect/platform-node": "4.0.1", - "effect": "4.0.1", - "js-yaml": "4.2.0" - }, - "devDependencies": { - "@types/js-yaml": "^4.0.9", - "@types/node": "25.9.1", - "typescript": "npm:@typescript/native-preview@beta", - "vitest": "^4.1.7" - }, - "typesVersions": { - "*": { - "*": [ - "dist/*" - ] - } - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./argv-patch": { - "types": "./dist/argv-patch.d.ts", - "default": "./dist/argv-patch.js" - }, - "./extract": { - "types": "./dist/extract.d.ts", - "default": "./dist/extract.js" - }, - "./gist": { - "types": "./dist/gist.d.ts", - "default": "./dist/gist.js" - }, - "./os-command": { - "types": "./dist/os-command.d.ts", - "default": "./dist/os-command.js" - }, - "./shared": { - "types": "./dist/shared.d.ts", - "default": "./dist/shared.js" - }, - "./sync-effect-subtree": { - "types": "./dist/sync-effect-subtree.d.ts", - "default": "./dist/sync-effect-subtree.js" - }, - "./sync-effect-app-subtree": { - "types": "./dist/sync-effect-app-subtree.d.ts", - "default": "./dist/sync-effect-app-subtree.js" - }, - "./sync-shared": { - "types": "./dist/sync-shared.d.ts", - "default": "./dist/sync-shared.js" - } - }, - "scripts": { - "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && pnpm check", - "check": "tsgo --build", - "build": "pnpm build:tsc", - "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsgo -w", - "clean": "rm -rf dist", - "clean-dist": "sh ../../scripts/clean-dist.sh", - "circular": "pnpm circular:src && pnpm circular:dist", - "circular:src": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", - "circular:dist": "madge --circular --extensions js ./dist", - "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsgo --noEmit", - "lint": "oxlint --quiet --type-aware ./src && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --quiet --type-aware --fix ./src && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "test": "vitest", - "test:run": "pnpm run test run --passWithNoTests", - "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", - "ncu": "ncu", - "pub": "pnpm publish --access public", - "prepack": "node ../../scripts/stagePublishPackage.mjs prepack", - "postpack": "node ../../scripts/stagePublishPackage.mjs postpack", - "postpublish": "node ../../scripts/stagePublishPackage.mjs postpublish" - }, - "gitHead": "bd8e27eea3eff97db8739d577d67e7336c078d28", - "sideEffects": [ - "./dist/faker.js", - "./faker", - "./src/faker", - "./dist/faker", - "./dist/src/faker.js", - "./dist/faker.js" - ], - "publishConfig": { - "directory": ".publish", - "linkDirectory": false - } -} diff --git a/packages/cli/src/argv-patch.ts b/packages/cli/src/argv-patch.ts deleted file mode 100644 index 242c56132..000000000 --- a/packages/cli/src/argv-patch.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Join wrap args into a single argv element so that -// `effect-app-cli index-multi tsc --build` works without quoting. -// The new effect/cli lexer classifies --flags as LongOption tokens -// and discards unrecognized ones at the subcommand level. -// By joining all args after the subcommand into one string, the lexer -// sees a single Value token instead of separate LongOption tokens. - -const wrapCommands = new Set(["index-multi", "packagejson", "packagejson-packages"]) - -export const patchArgvForWrapCommands = (argv: Array): void => { - const subIdx = argv.findIndex((a, i) => i >= 2 && wrapCommands.has(a)) - if (subIdx === -1 || subIdx + 1 >= argv.length) return - - const wrapArgs = argv.splice(subIdx + 1) - argv.push(wrapArgs.join(" ")) -} diff --git a/packages/cli/src/extract.ts b/packages/cli/src/extract.ts deleted file mode 100644 index ecc940ae2..000000000 --- a/packages/cli/src/extract.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as EffectArray from "effect/Array" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import { pipe } from "effect/Function" -import * as Order from "effect/Order" -import * as Path from "effect/Path" -import type * as PlatformError from "effect/PlatformError" - -/** - * Generates package.json exports mappings for TypeScript modules - * This function finds all .ts files in src/ (excluding test files) and generates - * JSON export entries that map source files to their compiled .js and .d.ts outputs - * - * Example output: - * "./utils/helper": { "types": "./dist/utils/helper.d.ts", "default": "./dist/utils/helper.js" }, - * - * This allows users to import individual modules instead of the entire package: - * import { helper } from 'package/utils/helper' instead of 'package' - */ -export const ExtractExportMappingsService = Effect.fn("effa-cli.extractExportMappings")(function*() { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - - return Effect.fn("effa-cli.extractExportMappings.service")(function*(cwd: string) { - // @effect-diagnostics-next-line effectFnOpportunity:off - const findTsFiles = (dir: string): Effect.Effect => - Effect.gen(function*() { - const entries = yield* fs.readDirectory(dir) - - const results = yield* Effect.all( - entries.map(Effect.fnUntraced(function*(entry) { - const fullPath = path.join(dir, entry) - const stat = yield* fs.stat(fullPath) - - if (stat.type === "Directory") { - return yield* findTsFiles(fullPath) - } else if (entry.endsWith(".ts") && !entry.includes(".test.")) { - return [fullPath] - } - return [] - })) - ) - - return EffectArray.flatten(results) - }) - - const srcDir = path.join(cwd, "src") - - // Check if src directory exists - const srcExists = yield* fs.exists(srcDir) - if (!srcExists) { - return "" - } - - const tsFiles = yield* findTsFiles(srcDir) - - const exportMappings = tsFiles.map((filePath) => { - // Get relative path from src directory (like bash script's cut -c 5-) - const relativePath = path.relative(srcDir, filePath) - const exportKey = `./${relativePath.replace(/\.ts$/, "")}` - const distPath = `./dist/${relativePath.replace(/\.ts$/, ".js")}` - const typesPath = `./dist/${relativePath.replace(/\.ts$/, ".d.ts")}` - - return `"${exportKey}": { "types": "${typesPath}", "default": "${distPath}" }` - }) - - const sortedMappings = pipe( - exportMappings, - EffectArray.sort(Order.String), - EffectArray.join(",\n") - ) - - return sortedMappings - }) -})() diff --git a/packages/cli/src/gist.ts b/packages/cli/src/gist.ts deleted file mode 100644 index 929cf41f8..000000000 --- a/packages/cli/src/gist.ts +++ /dev/null @@ -1,748 +0,0 @@ -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -// import necessary modules from the libraries -import * as Array from "effect/Array" -import * as Config from "effect/Config" -import * as Context from "effect/Context" -import * as Data from "effect/Data" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import { pipe } from "effect/Function" -import * as Layer from "effect/Layer" -import * as Option from "effect/Option" -import * as Path from "effect/Path" -import * as Redacted from "effect/Redacted" -import * as Result from "effect/Result" -import * as Schema from "effect/Schema" -import * as SchemaIssue from "effect/SchemaIssue" -import * as SchemaTransformation from "effect/SchemaTransformation" -import * as SynchronizedRef from "effect/SynchronizedRef" - -import * as yaml from "js-yaml" -import path from "path" -import { RunCommandService } from "./os-command.js" - -export class GistError extends Data.TaggedError("GistError")<{ message: string }> {} - -// -// -// Schemas -// - -export class GistEntry extends Schema.Opaque()(Schema.Struct({ - description: Schema.String, - public: Schema.Boolean, - company: Schema.String, - files: Schema.Array(Schema.String) -})) {} - -/** - * Extended gist entry that validates file_name uniqueness and extracts base filenames. - * - * GitHub Gists have a flat file structure and do not support directories/folders. - * All files within a gist must exist in the same namespace, meaning that files - * with identical names will collide, even if they originate from different local - * directories. When multiple files share the same basename, GitHub will either: - * - Reject the gist creation - * - Silently overwrite files (last one wins) - * - Display unpredictable behavior - * - * This validation prevents such collisions by detecting when multiple file paths - * would result in the same file_name when flattened to the gist structure. - * - * @example - * // These paths would collide in a gist - * ["src/config.json", "dist/config.json"] // Both become "config.json" - * - * @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 Schema.Opaque()( - 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.transformEffect({ - 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 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 a combined message - if (messages.length > 0) { - return yield* Effect.fail( - new SchemaIssue.InvalidValue({ message: messages.join("; ") }, entry.files) - ) - } - - return yield* Effect.succeed({ ...entry, files_with_name }) - }), - encode: ({ files_with_name: _, ...entry }) => Effect.succeed(entry) - }) - ) - ) -) {} - -export class GistYAML extends Schema.Opaque()(Schema.Struct({ - gists: Schema.optional(Schema.NullOr( - Schema.Record(Schema.String, GistEntryDecoded) - )), - settings: Schema.Struct({ - token_env: Schema.String, - base_directory: Schema.String - }) -})) {} - -/** - * Cache entry representing a gist mapping. - * Each entry contains the gist's human-readable name and GitHub ID. - */ -export class GistCacheEntry extends Schema.Opaque()(Schema.Struct({ - name: Schema.String, - id: Schema.String -})) {} - -export const GistCacheEntries = Schema.Array(GistCacheEntry) -export interface GistCacheEntries extends Schema.Schema.Type {} - -/** - * Gist cache mapping YAML configuration names to GitHub gist IDs with company awareness. - * - * Since GitHub gists don't have user-defined names, we maintain a cache - * that maps the human-readable names from our YAML config to actual gist IDs. - * Each cache entry is associated with a company, enabling multi-tenant operations. - * This allows us to: - * - Update existing gists instead of creating duplicates - * - Clean up obsolete entries when gists are removed from config - * - Persist the name->ID mapping across CLI runs - * - Isolate gist operations by company context - * - * The cache itself is stored as a secret GitHub gist for persistence. - */ -export class GistCache { - entries: GistCacheEntries - gist_id: string - - company: string - - constructor({ company, entries, gist_id }: { entries: GistCacheEntries; gist_id: string; company: string }) { - this.entries = entries - this.gist_id = gist_id - this.company = company - } -} - -// -// -// Errors -// -class GistYAMLError extends Data.TaggedError("GistYAMLError")<{ - readonly message: string -}> {} - -// -// -// Services -// - -export class GHGistService extends Context.Service()("GHGistService", { - make: Effect.gen(function*() { - 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.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`), - Effect.orDie, - Effect.asVoid - ) - } - - // 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.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`), - Effect.orDie - ) - } - - /** - * Extracts the Gist ID from a given GitHub Gist URL: https://gist.github.com/user/ID - * @param url - The full URL of the GitHub Gist. - * @returns An Option containing the Gist ID if extraction is successful, otherwise None. - */ - function extractGistIdFromUrl(url: string) { - const gist_id = url.trim().split("/").pop() - return gist_id && gist_id.length > 0 ? Option.some(gist_id) : Option.none() - } - - const loadGistCache = Effect.fn("effa-cli.gist.loadGistCache")( - function*(cacheGistId: string, company: string) { - const filesInCache = yield* runGetStringSuppressed(`gh gist view ${cacheGistId} --files`).pipe( - Effect.map((files) => - files - .trim() - .split("\n") - .map((f) => f.trim()) - ) - ) - - if (!filesInCache.includes(`${company}.json`)) { - yield* Effect.logInfo(`Cache for company ${company} not found, creating company-specific cache file...`) - yield* runGetStringSuppressed( - `echo "[]" | gh gist edit ${cacheGistId} -a ${company}.json -` - ) - return new GistCache({ entries: [], gist_id: cacheGistId, company }) - } - - const cacheContent = yield* runGetStringSuppressed(`gh gist view ${cacheGistId} -f "${company}.json"`) - - const entries = yield* pipe( - cacheContent, - Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(GistCacheEntries))), - Effect.orDie - ) - - return new GistCache({ entries, gist_id: cacheGistId, company }) - } - ) - - const saveGistCache = Effect.fn("effa-cli.gist.saveGistCache")( - function*(cache: GistCache) { - const cacheJson = yield* pipe( - cache.entries, - Schema.encodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(GistCacheEntries))), - // cannot recover from parse errors in any case, better to die here instead of cluttering the signature - Effect.orDie - ) - - yield* runGetExitCodeSuppressed( - `echo '${cacheJson}' | gh gist edit ${cache.gist_id} -f ${cache.company}.json -` - ) - } - ) - - const createGistWithFiles = Effect.fn("GHGistService.createGistWithFiles")( - function*({ description, env, files, gist_name, is_public }: { - gist_name: string - description: string - files: { - path: string - name: string - }[] - is_public: boolean - env: string - }) { - yield* Effect.logInfo(`Creating gist ${gist_name} with ${files.length} file(s)`) - - const ghCommand = [ - "gh", - "gist", - "create", - `--desc="${description}"`, - is_public ? "--public" : "", - ...files.map((file) => `"${file.path}"`) - ] - .filter((x) => !!x) - .join(" ") - - // create and capture the created gist URL - const gistUrl = yield* runGetStringSuppressed(ghCommand) - - // extract ID from URL - const gistNameId = yield* pipe( - gistUrl, - extractGistIdFromUrl, - Option.match({ - onNone: () => Effect.die(`Failed to extract gist ID from URL: ${gistUrl}`), - onSome: (id) => - Effect - .succeed( - { name: gist_name, id } - ) - .pipe(Effect.tap(Effect.logInfo(`Created gist with ID ${id}`))) - }) - ) - - // rename all files to include environment prefix for multi-environment support - for (const file of files) { - const originalName = file.name - const name_with_env = `${env}.${originalName}` - const ghRenameCommand = [ - "gh", - "gist", - "rename", - gistNameId.id, - originalName, - name_with_env - ] - .join(" ") - - yield* Effect.logInfo(`Renaming file ${originalName} to ${name_with_env} in gist ${gist_name}`) - yield* runGetStringSuppressed(ghRenameCommand) - } - - return gistNameId - } - ) - - const getGistFileNames = Effect.fn("getGistFileNames")( - function*({ env, gist_id, gist_name }: { - gist_id: string - gist_name: string - env: string - }) { - yield* Effect.logInfo(`Retrieving file names from gist ${gist_name} with ID ${gist_id}`) - const output = yield* runGetStringSuppressed(`gh gist view ${gist_id} --files`) - - // 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) => { - const fnTrimmed = fn.trim() - if (!fnTrimmed.startsWith(env + ".")) return Result.fail(fn) - return Result.succeed(fnTrimmed.substring(env.length + 1)) // remove env prefix and dot - }) - } - ) - - const removeFileFromGist = Effect.fn("removeFileFromGist")( - function*({ env, file_name, gist_id, gist_name }: { - gist_id: string - gist_name: string - file_name: string - env: string - }) { - const name_with_env = `${env}.${file_name}` - yield* Effect.logInfo(`Removing file ${name_with_env} from gist ${gist_name}`) - return yield* runGetExitCodeSuppressed(`gh gist edit ${gist_id} --remove "${name_with_env}"`) - } - ) - - const updateFileOfGist = Effect.fn("updateFileOfGist")( - function*({ env, file_name, file_path, gist_id, gist_name }: { - gist_id: string - gist_name: string - file_name: string - file_path: string - env: string - }) { - const name_with_env = `${env}.${file_name}` - yield* Effect.logInfo(`Updating file ${name_with_env} located at ${file_path} of gist ${gist_name}`) - - // it seems this does not require renaming the file - const editCommand = [ - "gh", - "gist", - "edit", - gist_id, - "-f", - name_with_env, - `"${file_path}"` - ] - .join(" ") - - return yield* runGetExitCodeSuppressed(editCommand) - } - ) - - const addFileToGist = Effect.fn("addFileToGist")( - function*({ env, file, gist_id, gist_name }: { - gist_id: string - gist_name: string - file: { - path: string - name: string - } - env: string - }) { - yield* Effect.logInfo(`Adding file ${file.path} to gist ${gist_name}`) - const editCommand = [ - "gh", - "gist", - "edit", - gist_id, - "-a", - `"${file.path}"` - ] - .join(" ") - - yield* runGetExitCodeSuppressed(editCommand) - - const renameCommand = [ - "gh", - "gist", - "rename", - gist_id, - file.name, - `${env}.${file.name}` - ] - .join(" ") - - yield* Effect.logInfo(`Renaming file ${file.name} to ${env}.${file.name} in gist ${gist_name}`) - return yield* runGetExitCodeSuppressed(renameCommand) - } - ) - - const deleteGist = Effect.fn("deleteGist")( - function*({ gist_id, gist_name }: { gist_id: string; gist_name: string }) { - yield* Effect.logInfo(`Deleting gist ${gist_name} with ID ${gist_id}`) - return yield* runGetExitCodeSuppressed(`gh gist delete ${gist_id}`) - } - ) - - const login = Effect.fn("GHGistService.login")(function*(token: string) { - if ((yield* runGetExitCode("gh --version").pipe(Effect.orDie)) !== 0) { - return yield* Effect.die( - "GitHub CLI (gh) is not installed or not found in PATH. Please install it to use the gist command." - ) - } - - const isLogged = yield* runGetExitCode(`echo ${token} | gh auth login --with-token`).pipe(Effect.orDie) - if (isLogged !== 0) { - return yield* new GistError({ message: "Failed to log in to GitHub CLI with provided token" }) - } else { - yield* Effect.logInfo("Successfully logged in to GitHub CLI") - } - }) - - return { - /** Logs into GitHub using the GitHub CLI. - * This is a prerequisite for other gist operations. - * @param token - The GitHub personal access token with gist permissions - * - * @returns An Effect that succeeds when login is successful - */ - login, - - /** - * Loads company mappings from the configured cache Gist. - * A missing company file is initialized once with an empty mapping. - * - * @returns An Effect that yields the loaded company cache - */ - loadGistCache, - - /** - * Saves the current gist cache state to the GitHub cache gist. - * Updates the existing cache gist with the current mappings of names to gist IDs. - * - * @param cache - The GistCache instance to save - * @returns An Effect that succeeds when the cache is successfully saved - */ - saveGistCache, - - /** - * Creates a new GitHub gist with the specified files and renames them with environment prefixes. - * Generates a GitHub CLI command to create the gist, extracts the resulting gist ID, - * and renames all files with environment prefixes for multi-environment support. - * - * @param gist_name - The human-readable name for this gist (used in cache mapping) - * @param description - The description for the GitHub gist - * @param files - Array of file objects with path and name properties to include in the gist - * @param is_public - Whether the gist should be public or private - * @param env - Environment prefix to prepend to file names (e.g., "local-dev", "prod") - * @returns An Effect that yields a gist entry object with name and id properties - */ - createGistWithFiles, - - /** - * Retrieves file names from a GitHub gist, filtered by environment prefix. - * Fetches the list of files contained in the specified gist and returns only - * those that match the current environment, with the environment prefix removed. - * - * @param gist_id - The GitHub gist ID to retrieve file names from - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param env - Environment prefix to filter files by (e.g., "local-dev", "prod") - * @returns An Effect that yields an array of file names with environment prefix removed - */ - getGistFileNames, - - /** - * Removes a file from a specified GitHub gist. - * The file name is automatically prefixed with the environment when removing. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file_name - The base name of the file to remove (without environment prefix) - * @param env - Environment prefix that was used when the file was added - * @returns An Effect that succeeds when the file is removed - */ - removeFileFromGist, - - /** - * Updates a file in a specified GitHub gist. - * The file name is automatically prefixed with the environment when updating. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file_name - The base name of the file to update (without environment prefix) - * @param file_path - The local path of the file to update in the gist - * @param env - Environment prefix that was used when the file was added - * @returns An Effect that succeeds when the file is updated - */ - updateFileOfGist, - - /** - * Adds a new file to a specified GitHub gist. - * The file is automatically renamed with an environment prefix for multi-environment support. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file - The file object containing path and name properties - * @param env - Environment prefix to prepend to the file name - * @returns An Effect that succeeds when the file is added and renamed - */ - addFileToGist, - - /** - * Deletes a specified GitHub gist by its ID. - * @param gist_id - The ID of the gist to delete - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @returns An Effect that succeeds when the gist is deleted - */ - deleteGist - } - }) -}) { - static DefaultWithoutDependencies = Layer.effect(this, this.make) - static Default = this.DefaultWithoutDependencies.pipe( - Layer.provide(RunCommandService.Default) - ) -} - -export class GistHandler extends Context.Service()("GistHandler", { - make: Effect.gen(function*() { - const GH = yield* GHGistService - - // I prefer to provide these two only once during the main CLI pipeline setup - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - - return { - handler: Effect.fn("effa-cli.gist.GistHandler")(function*({ YAMLPath }: { YAMLPath: string }) { - // load company and environment from environment variables - const CONFIG = yield* Config.all({ - company: Config.String("COMPANY"), - env: Config.String("ENV").pipe(Config.withDefault("local-dev")), - gistCacheId: Config.NonEmptyString("EFFA_GIST_CACHE_ID") - }) - - yield* Effect.logInfo(`Company: ${CONFIG.company}, ENV: ${CONFIG.env}`) - - yield* Effect.logInfo(`Reading configuration from ${YAMLPath}`) - - const configExists = yield* fs.exists(YAMLPath) - if (!configExists) { - return yield* new GistError({ message: `Configuration file not found: ${YAMLPath}` }) - } - - const configFromYaml = yield* pipe( - YAMLPath, - fs.readFileString, - Effect.andThen((content) => - Effect.try({ - try: () => yaml.load(content), - catch(error) { - return new GistYAMLError({ message: `Failed to parse YAML: ${(error as Error).message}` }) - } - }) - ), - Effect.andThen(Schema.decodeUnknownEffect(GistYAML)) - ) - - // load GitHub token securely from environment variable - const redactedToken = yield* Config.Redacted(configFromYaml.settings.token_env) - - yield* Effect.logInfo(`Using GitHub token from environment variable: ${configFromYaml.settings.token_env}`) - yield* Effect.logInfo(`Token loaded: ${redactedToken}`) // this will show in logs - - yield* GH.login(Redacted.value(redactedToken)) - - const cache = yield* SynchronizedRef.make( - yield* GH.loadGistCache(CONFIG.gistCacheId, CONFIG.company) - ) - - // 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 ?? {}) - .filter(([, v]) => v.company === CONFIG.company) - - for ( - const [name, gistConfig] of thisCompanyGistsFromYaml - ) { - const { description, files_with_name, public: is_public } = gistConfig - - yield* Effect.logInfo(`Processing gist ${name}`) - - const filesOnDiskWithFullPath = yield* Effect - .all( - files_with_name.map(Effect.fnUntraced(function*(f) { - const fullPath = path.join(configFromYaml.settings.base_directory, f.path) - const fileExists = yield* fs.exists(fullPath) - - if (!fileExists) { - yield* Effect.logWarning(`File not found: ${fullPath}, skipping...`) - return Option.none() - } - - return Option.some({ - path: fullPath, - name: f.name - }) - })), - { - concurrency: "unbounded" - } - ) - .pipe(Effect.map(Array.getSomes)) - - const gistFromCache = (yield* SynchronizedRef.get(cache)).entries.find((_) => _.name === name) - - // if the gist's name exists in cache, update the existing gist - // otherwise, create a new gist and update the local cache - if (gistFromCache) { - yield* Effect.logInfo(`Updating existing gist ${gistFromCache.name} with ID ${gistFromCache.id}`) - - // get current files in the gist to detect removed files - const gistFileNames = new Set( - yield* GH.getGistFileNames({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - env: CONFIG.env - }) - ) - - const expectedFiles = new Set(filesOnDiskWithFullPath.map(({ name }) => name)) - - // remove files that are no longer in YAML configuration - for (const gf of gistFileNames) { - if (!expectedFiles.has(gf)) { - yield* GH.removeFileFromGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file_name: gf, - env: CONFIG.env - }) - } - } - - // update/add files from configuration - for (const f of filesOnDiskWithFullPath) { - if (gistFileNames.has(f.name)) { - yield* GH.updateFileOfGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file_name: f.name, - file_path: f.path, - env: CONFIG.env - }) - } else { - yield* GH.addFileToGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file: f, - env: CONFIG.env - }) - } - } - } else { - if (filesOnDiskWithFullPath.length !== 0) { - yield* SynchronizedRef.getAndUpdateEffect( - cache, - Effect.fnUntraced(function*(cache) { - return new GistCache({ - gist_id: cache.gist_id, - entries: [ - ...cache.entries, - { - ...(yield* GH.createGistWithFiles({ - gist_name: name, - description, - is_public, - - files: filesOnDiskWithFullPath, - env: CONFIG.env - })) - } - ], - company: cache.company - }) - }) - ) - } else { - yield* Effect.logWarning(`No valid files found for gist ${name}, skipping creation...`) - } - } - } - - // cache cleanup: remove gists that are no longer in YAML configuration - // only affects entries for the current company to maintain isolation - const configGistNames = new Set( - thisCompanyGistsFromYaml - .map(([name]) => name) - ) - - const newCache = yield* SynchronizedRef.updateAndGetEffect( - cache, - Effect.fnUntraced(function*(cache) { - const newEntries = [...cache.entries] - - // remove obsolete cache entries for current company only - // this ensures gists from other companies remain untouched - for (let i = newEntries.length - 1; i >= 0; i--) { - const cacheEntry = newEntries[i] - if (cacheEntry && !configGistNames.has(cacheEntry.name)) { - // delete the actual gist from GitHub - yield* GH.deleteGist({ - gist_id: cacheEntry.id, - gist_name: cacheEntry.name - }) - yield* Effect.logInfo( - `Obsolete gist ${cacheEntry.name} of company ${CONFIG.company} with ID ${cacheEntry.id}) will be removed from cache` - ) - newEntries.splice(i, 1) - } - } - - return { ...cache, entries: newEntries } - }) - ) - - yield* GH.saveGistCache(newCache) - - yield* Effect.logInfo("Gist operations completed") - }) - } - }) -}) { - 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 deleted file mode 100644 index 53c2c2f7e..000000000 --- a/packages/cli/src/index.ts +++ /dev/null @@ -1,903 +0,0 @@ -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -// import necessary modules from the libraries -import { NodeRuntime, NodeServices } from "@effect/platform-node" -import { Argument, Command, Flag, Prompt } from "effect/cli" - -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Layer from "effect/Layer" -import * as Option from "effect/Option" -import * as Path from "effect/Path" -import * as Stream from "effect/Stream" -import { ExtractExportMappingsService } from "./extract.js" -import { GistHandler } from "./gist.js" -import { RunCommandService } from "./os-command.js" -import { packages } from "./shared.js" - -import { patchArgvForWrapCommands } from "./argv-patch.js" -import { syncEffectAppSubtree } from "./sync-effect-app-subtree.js" -import { syncEffectSubtree } from "./sync-effect-subtree.js" -import { syncDiff, syncPush, syncShared } from "./sync-shared.js" -import { makeNcuUpdateCommand } from "./update-packages.js" - -patchArgvForWrapCommands(process.argv) - -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 within their existing semver ranges 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(makeNcuUpdateCommand(filter)) - yield* runGetExitCode(makeNcuUpdateCommand(filter, { recursive: true })) - } - })() - - /** - * Updates Effect ecosystem packages within their existing semver ranges 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(makeNcuUpdateCommand(filter)) - yield* runGetExitCode(makeNcuUpdateCommand(filter, { recursive: true })) - } - })() - - /** - * 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 trailingNewline = packageJsonContent.endsWith("\n") ? "\n" : "" - 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}` - return acc - }, {} as Record) - } - - pj.resolutions = resolutions - - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2) + trailingNewline) - 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 trailingNewline = packageJsonContent.endsWith("\n") ? "\n" : "" - 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 - ) - - pj.resolutions = filteredResolutions - - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2) + trailingNewline) - 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 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 - ) - } - ) - - /** - * 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 }) - } - ) - - /** - * 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] - return prev - }, - {} 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) => { - // 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 - if (cur !== "./index" && !cur.includes("/internal/")) { - prev[cur] = filteredExportEntries[cur] - } - return prev - }, - {} as Record - ) - } - - const pkgJsonContent = yield* fs.readFileString(p + "/package.json", "utf-8") - const trailingNewline = pkgJsonContent.endsWith("\n") ? "\n" : "" - const pkgJson = JSON.parse(pkgJsonContent) - - pkgJson.exports = packageExports - - yield* Effect.logInfo(`Writing updated package.json for ${p}`) - - return yield* fs.writeFileString( - p + "/package.json", - JSON.stringify(pkgJson, null, 2) + trailingNewline - ) - } - ) - - /** - * 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 - } - - const watchStream = fs.watch(srcPath) - - 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 - ) - } - ) - - /* - * CLI - */ - - 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" - ) - ) - - // 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" - ) - ) - - /** - * 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> - } - - if (completionMessage) { - yield* Effect.addFinalizer(() => Effect.logInfo(completionMessage)) - } - - const wrapOption = Option.orElse(wa, () => wo) - - yield* handler(cfg as any) - - if (Option.isSome(wrapOption)) { - const val: string = Array.isArray(wrapOption.value) - ? wrapOption.value.join(" ") - : wrapOption.value as string - - yield* Effect.logInfo(`Spawning child command: ${val}`) - const exitCode = yield* runGetExitCode(val) - if (exitCode !== 0) { - return yield* Effect.sync(() => process.exit(exitCode)) - } - } - - return - }, (_) => Effect.scoped(_)) - ) - - 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") - ) - - 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" - }) - - 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")) - - const up = Command - .make( - "up", - {}, - 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")) - - 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`) - } - } - - 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() - - 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 packagejsonPackages = makeCommandWithWrap( - "packagejson-packages", - {}, - Effect.fn("effa-cli.packagejson-packages")(function*({}) { - // https://nodejs.org/api/path.html#pathresolvepaths - const startDir = path.resolve() - - const packagesDir = path.join(startDir, "packages") - - 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) - } - } - - 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) - }) - ) - ) - - 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: Flag.File("config").pipe( - Flag.withDefault("gists.yaml"), - Flag.withDescription("Path to YAML configuration file") - ) - }, - 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 (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 \\)" - ) - } 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") - }) - ) - .pipe(Command.withDescription("Nuclear cleanup command: removes all generated files and cleans the workspace")) - - const syncEffect = Command - .make( - "sync-effect", - { - manifests: Flag.String("manifests").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription( - "Comma-separated list of package.json paths to scan (default: package.json)" - ) - ), - prefix: Flag.String("prefix").pipe( - Flag.optional, - Flag.withDescription("Subtree prefix (default: repos/effect)") - ), - url: Flag.String("url").pipe( - Flag.optional, - Flag.withDescription( - "Git repository URL (default: https://github.com/Effect-TS/effect.git)" - ) - ) - }, - Effect.fn("effa-cli.sync-effect")(function*({ manifests, prefix, url }) { - yield* syncEffectSubtree({ - manifestPaths: Option.match(manifests, { - onNone: () => ["package.json"], - onSome: (m) => m.split(",").map((p) => p.trim()) - }), - subtreePrefix: Option.getOrElse(prefix, () => "repos/effect"), - url: Option.getOrElse(url, () => "https://github.com/Effect-TS/effect.git") - }) - }) - ) - .pipe(Command.withDescription("Sync the Effect subtree to the version pinned in package.json")) - - const syncEffectApp = Command - .make( - "sync-effect-app", - { - manifests: Flag.String("manifests").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription( - "Comma-separated list of package.json paths to scan (default: package.json)" - ) - ), - prefix: Flag.String("prefix").pipe( - Flag.optional, - Flag.withDescription("Subtree prefix (default: repos/libs)") - ), - url: Flag.String("url").pipe( - Flag.optional, - Flag.withDescription( - "Git repository URL (default: https://github.com/effect-app/libs.git)" - ) - ), - ref: Flag.String("ref").pipe( - Flag.optional, - Flag.withDescription("Ref escape hatch (branch/tag/sha/latest); latest means main") - ) - }, - Effect.fn("effa-cli.sync-effect-app")(function*({ manifests, prefix, ref, url }) { - yield* syncEffectAppSubtree({ - manifestPaths: Option.match(manifests, { - onNone: () => ["package.json"], - onSome: (m) => m.split(",").map((p) => p.trim()) - }), - ...Option.match(ref, { - onNone: () => ({}), - onSome: (ref) => ({ ref }) - }), - subtreePrefix: Option.getOrElse(prefix, () => "repos/libs"), - url: Option.getOrElse(url, () => "https://github.com/effect-app/libs.git") - }) - }) - ) - .pipe(Command.withDescription("Sync the Effect App libs subtree to the version pinned in package.json")) - - const SharedLockfileFlag = Flag.File("lockfile").pipe( - Flag.optional, - Flag.withDescription("Path to lockfile (default: .shared.json)") - ) - - const sync = Command - .make( - "sync", - { - lockfile: SharedLockfileFlag, - update: Flag.Boolean("update").pipe( - Flag.withDescription("Bump the pinned ref to the latest sha before syncing") - ), - ref: Flag.String("ref").pipe( - Flag.optional, - Flag.withDescription("Ref (branch/tag/sha) to update to; default: remote default branch HEAD") - ) - }, - Effect.fn("effa-cli.sync")(function*({ lockfile, ref, update }) { - yield* syncShared({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json"), - update, - ref: Option.getOrUndefined(ref) - }) - }) - ) - .pipe( - Command.withDescription("Sync shared docs / e2e helpers / plugins from effect-app/shared per .shared.json") - ) - - const syncDiffCmd = Command - .make( - "sync-diff", - { lockfile: SharedLockfileFlag }, - Effect.fn("effa-cli.sync-diff")(function*({ lockfile }) { - yield* syncDiff({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json") - }) - }) - ) - .pipe(Command.withDescription("Report drift between local synced files and the pinned shared ref")) - - const syncPushCmd = Command - .make( - "sync-push", - { - lockfile: SharedLockfileFlag, - message: Flag.String("message").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription("Commit message for the push") - ), - branch: Flag.String("branch").pipe( - Flag.optional, - Flag.withDescription("Branch name in shared repo (default: auto-generated)") - ), - pr: Flag.Boolean("pr").pipe( - Flag.withDescription("Open a PR via `gh pr create` after pushing") - ) - }, - Effect.fn("effa-cli.sync-push")(function*({ branch, lockfile, message, pr }) { - yield* syncPush({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json"), - message: Option.getOrUndefined(message), - branch: Option.getOrUndefined(branch), - pr - }) - }) - ) - .pipe(Command.withDescription("Push locally-modified synced files to the shared repo on a new branch")) - - // configure CLI - return yield* Command.run( - Command - .make("effa") - .pipe(Command.withSubcommands([ - ue, - up, - link, - unlink, - indexMulti, - packagejson, - packagejsonPackages, - gist, - nuke, - syncEffect, - syncEffectApp, - sync, - syncDiffCmd, - syncPushCmd - ])), - { - version: "v1.0.0" - } - ) - })() - .pipe( - Effect.scoped, - Effect.provide( - Layer.provideMerge( - Layer.merge( - GistHandler.Default, - RunCommandService.Default - ), - NodeServices.layer - ) - ) - ) -) diff --git a/packages/cli/src/os-command.ts b/packages/cli/src/os-command.ts deleted file mode 100644 index 9982a34cc..000000000 --- a/packages/cli/src/os-command.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -import * as Context from "effect/Context" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import { ChildProcess } from "effect/process" -import { ChildProcessSpawner } from "effect/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. - */ -export class RunCommandService extends Context.Service()("RunCommandService", { - make: Effect.gen(function*() { - // will be provided by the main CLI pipeline setup - const spawner = yield* ChildProcessSpawner - - /** - * Executes a shell command using Command API with inherited stdio streams. - * 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) => - spawner - .exitCode( - ChildProcess.make("sh", ["-c", cmd], { stdout: "inherit", stderr: "inherit", cwd }) - ) - - /** - * Executes a shell command using Command API and returns the output as a string. - * 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 command's stdout output as string or fails with a PlatformError - */ - const runGetString = (cmd: string, cwd?: string) => - spawner - .string( - ChildProcess.make("sh", ["-c", cmd], { cwd }) - ) - - return { - runGetExitCode, - runGetString - } - }) -}) { - static Default = Layer.effect(this, this.make) -} diff --git a/packages/cli/src/shared.ts b/packages/cli/src/shared.ts deleted file mode 100644 index 6aade80bb..000000000 --- a/packages/cli/src/shared.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const packages = ["effect", "@effect/platform", "@effect/platform-node"] - -export const EFFECT_APP_LIBS = process.env["EFFECT_APP_LIBS"] || "../../effect-app/libs" diff --git a/packages/cli/src/sync-effect-app-subtree.ts b/packages/cli/src/sync-effect-app-subtree.ts deleted file mode 100644 index 59e9cbb56..000000000 --- a/packages/cli/src/sync-effect-app-subtree.ts +++ /dev/null @@ -1,307 +0,0 @@ -import * as Arr from "effect/Array" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Option from "effect/Option" -import * as Path from "node:path" -import { RunCommandService } from "./os-command.js" - -export interface SyncEffectAppConfig { - readonly manifestPaths: ReadonlyArray - readonly subtreePrefix: string - readonly url: string - readonly ref?: string -} - -interface VersionMention { - readonly manifestPath: string - readonly section: string - readonly name: string - readonly range: string - readonly normalized: string -} - -const depSections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"] as const - -const packageManifestPaths: Record = { - "effect-app": "packages/effect-app/package.json", - "@effect-app/infra": "packages/infra/package.json", - "@effect-app/vue": "packages/vue/package.json", - "@effect-app/vue-components": "packages/vue-components/package.json", - "@effect-app/cli": "packages/cli/package.json", - "@effect-app/eslint-codegen-model": "packages/eslint-codegen-model/package.json", - "@effect-app/eslint-shared-config": "packages/eslint-shared-config/package.json" -} - -const packagePreference = Object.keys(packageManifestPaths) - -const isRecord = (value: unknown): value is Record => - typeof value === "object" && value !== null && !Array.isArray(value) - -const isEffectAppPackage = (name: string) => name === "effect-app" || name.startsWith("@effect-app/") - -const isNonEmptyString = (value: string) => value !== "" - -const shellQuote = (value: string) => `'${value.replaceAll("'", "'\\''")}'` - -const normalizeVersion = (range: string) => { - const match = range.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/) - return match?.[0] -} - -const isFullSha = (ref: string) => /^[0-9a-f]{40}$/i.test(ref) - -const resolveRequestedRef = (ref: string) => ref === "latest" ? "main" : ref - -const parsePackageVersion = (source: string, packagePath: string) => { - const parsed: unknown = JSON.parse(source) - if (!isRecord(parsed) || typeof parsed["version"] !== "string") { - throw new Error(`Could not read version from ${packagePath}`) - } - return parsed["version"] -} - -const parseDefaultBranch = (source: string) => { - const line = source - .split("\n") - .find((line) => line.startsWith("ref: refs/heads/") && line.endsWith("\tHEAD")) - - if (!line) { - throw new Error("Could not resolve default branch for effect-app libs repository") - } - - return line.slice("ref: refs/heads/".length, -"\tHEAD".length) -} - -const selectVersionMentions = (mentions: Arr.NonEmptyArray) => { - for (const packageName of packagePreference) { - const selected = mentions.filter((mention) => mention.name === packageName) - if (Arr.isArrayNonEmpty(selected)) return selected - } - - return mentions -} - -const describeMentions = (mentions: ReadonlyArray) => - mentions - .map((mention) => `${mention.manifestPath} (${mention.section}) ${mention.name}: ${mention.range}`) - .join("\n") - -export const syncEffectAppSubtree = Effect.fnUntraced(function*(config: SyncEffectAppConfig) { - const fs = yield* FileSystem.FileSystem - const { runGetExitCode, runGetString } = yield* RunCommandService - - const run = Effect.fnUntraced(function*(command: string) { - const exitCode = yield* runGetExitCode(command) - if (exitCode !== 0) { - return yield* Effect.fail(new Error(`Command failed with exit code ${exitCode}: ${command}`)) - } - }) - - const fetchRemoteRef = Effect.fnUntraced(function*(remoteRef: string, localRef: string) { - const rawRef = `${localRef}-raw` - - yield* run( - `git fetch --no-tags ${shellQuote(config.url)} ${shellQuote(`+${remoteRef}:${rawRef}`)}` - ) - - const commit = (yield* runGetString(`git rev-list -n1 ${shellQuote(rawRef)}`)).trim() - yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(commit)}`) - }) - - const fetchRequestedRef = Effect.fnUntraced(function*(requestedRef: string, localRef: string) { - if (isFullSha(requestedRef)) { - const localCommit = yield* runGetString( - `git rev-parse --verify ${shellQuote(`${requestedRef}^{commit}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(localCommit)) { - return yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(localCommit.value.trim())}`) - } - } - - const remoteRefs = requestedRef.startsWith("refs/") - ? [requestedRef] - : [`refs/heads/${requestedRef}`, `refs/tags/${requestedRef}`, requestedRef] - - for (const remoteRef of remoteRefs) { - const exists = yield* runGetString( - `git ls-remote --exit-code ${shellQuote(config.url)} ${shellQuote(remoteRef)}` - ) - .pipe(Effect.option) - - if (Option.isSome(exists)) { - return yield* fetchRemoteRef(remoteRef, localRef) - } - } - - return yield* Effect.fail(new Error(`Could not resolve effect-app ref: ${requestedRef}`)) - }) - - if (config.ref) { - const ref = resolveRequestedRef(config.ref) - const syncRef = "refs/effa-sync/effect-app-requested" - yield* Effect.logInfo(`Using effect-app subtree ref: ${ref}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* fetchRequestedRef(ref, syncRef) - return yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) - } - - const versionMentions: Array = [] - - for (const manifestPath of config.manifestPaths) { - if (!(yield* fs.exists(manifestPath))) continue - - const manifest: unknown = JSON.parse(yield* fs.readFileString(manifestPath)) - if (!isRecord(manifest)) continue - - for (const section of depSections) { - const deps = manifest[section] - if (!isRecord(deps)) continue - - for (const [name, range] of Object.entries(deps)) { - if (!isEffectAppPackage(name) || typeof range !== "string") continue - - const normalized = normalizeVersion(range) - if (!normalized) continue - - versionMentions.push({ manifestPath, section, name, range, normalized }) - } - } - } - - if (!Arr.isArrayNonEmpty(versionMentions)) { - return yield* Effect.fail( - new Error("Could not find any effect-app/@effect-app dependency versions in manifests") - ) - } - - const selectedMentions = selectVersionMentions(versionMentions) - const versions = [...new Set(selectedMentions.map((mention) => mention.normalized))] - - if (Arr.isArrayNonEmpty(versions.slice(1))) { - return yield* Effect.fail( - new Error(`Found multiple Effect App versions. Please align first:\n${describeMentions(selectedMentions)}`) - ) - } - - const version = selectedMentions[0].normalized - const packageName = selectedMentions[0].name - const packagePath = packageManifestPaths[packageName] - - if (!packagePath) { - return yield* Effect.fail( - new Error(`No effect-app libs package manifest mapping for ${packageName}`) - ) - } - - const tagCandidates = [ - `${packageName}@${version}`, - `v${version}`, - version - ] - - let remoteRef: string | undefined - for (const candidate of tagCandidates) { - const tagResult = yield* runGetString( - `git ls-remote --exit-code --tags ${shellQuote(config.url)} ${shellQuote(`refs/tags/${candidate}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(tagResult)) { - remoteRef = candidate - break - } - } - - if (remoteRef) { - const syncRef = "refs/effa-sync/effect-app-tag" - yield* Effect.logInfo(`Using effect-app package: ${packageName}@${version}`) - yield* Effect.logInfo(`Using effect-app subtree ref: ${remoteRef}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* fetchRemoteRef(`refs/tags/${remoteRef}`, syncRef) - return yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) - } - - const defaultBranchOutput = yield* runGetString(`git ls-remote --symref ${shellQuote(config.url)} HEAD`) - const defaultBranch = yield* Effect.try({ - try: () => parseDefaultBranch(defaultBranchOutput), - catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)) - }) - - const cachePath = (yield* runGetString("git rev-parse --git-path effa-sync/effect-app-libs.git")).trim() - yield* run(`mkdir -p ${shellQuote(Path.dirname(cachePath))}`) - - if (!(yield* fs.exists(cachePath))) { - yield* run(`git init --bare ${shellQuote(cachePath)}`) - } - - yield* run( - `git --git-dir=${shellQuote(cachePath)} fetch --no-tags --prune ${shellQuote(config.url)} ${ - shellQuote(`+refs/heads/${defaultBranch}:refs/heads/${defaultBranch}`) - }` - ) - - const commits = (yield* runGetString( - `git --git-dir=${shellQuote(cachePath)} rev-list ${shellQuote(`refs/heads/${defaultBranch}`)}` - )) - .trim() - .split("\n") - .filter(isNonEmptyString) - - if (!Arr.isArrayNonEmpty(commits)) { - return yield* Effect.fail( - new Error(`No commits found on ${defaultBranch} for ${config.url}`) - ) - } - - let commit: string | undefined - for (const candidate of commits) { - const packageJsonResult = yield* runGetString( - `git --git-dir=${shellQuote(cachePath)} show ${shellQuote(`${candidate}:${packagePath}`)}` - ) - .pipe(Effect.option) - - if (Option.isNone(packageJsonResult)) continue - - const candidateVersion = yield* Effect.try({ - try: () => parsePackageVersion(packageJsonResult.value, packagePath), - catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)) - }) - - if (candidateVersion === version) { - commit = candidate - break - } - } - - if (!commit) { - return yield* Effect.fail( - new Error( - `No commit found on ${defaultBranch} where ${packagePath} has version ${version}. Tried tags: ${ - tagCandidates.join(", ") - }` - ) - ) - } - - const syncRef = "refs/effa-sync/effect-app-selected" - yield* run(`git --git-dir=${shellQuote(cachePath)} update-ref ${shellQuote(syncRef)} ${shellQuote(commit)}`) - - yield* Effect.logInfo(`Using effect-app package: ${packageName}@${version}`) - yield* Effect.logInfo(`Using effect-app subtree commit: ${commit}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} ${ - shellQuote(cachePath) - } ${shellQuote(syncRef)} --squash` - ) -}) diff --git a/packages/cli/src/sync-effect-subtree.ts b/packages/cli/src/sync-effect-subtree.ts deleted file mode 100644 index a307fb643..000000000 --- a/packages/cli/src/sync-effect-subtree.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Option from "effect/Option" -import { RunCommandService } from "./os-command.js" - -export interface SyncEffectConfig { - readonly manifestPaths: ReadonlyArray - readonly subtreePrefix: string - readonly url: string -} - -const depSections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"] as const - -const shellQuote = (value: string) => `'${value.replaceAll("'", "'\\''")}'` - -const normalizeVersion = (range: string) => { - const match = range.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/) - return match?.[0] -} - -export const syncEffectSubtree = Effect.fnUntraced(function*(config: SyncEffectConfig) { - const fs = yield* FileSystem.FileSystem - const { runGetExitCode, runGetString } = yield* RunCommandService - - const run = Effect.fnUntraced(function*(command: string) { - const exitCode = yield* runGetExitCode(command) - if (exitCode !== 0) { - return yield* Effect.fail(new Error(`Command failed with exit code ${exitCode}: ${command}`)) - } - }) - - const fetchRemoteRef = Effect.fnUntraced(function*(remoteRef: string, localRef: string) { - const rawRef = `${localRef}-raw` - - yield* run( - `git fetch --no-tags ${shellQuote(config.url)} ${shellQuote(`+${remoteRef}:${rawRef}`)}` - ) - - const commit = (yield* runGetString(`git rev-list -n1 ${shellQuote(rawRef)}`)).trim() - yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(commit)}`) - }) - - const versionMentions: Array<{ - manifestPath: string - section: string - name: string - range: string - normalized: string - }> = [] - - for (const manifestPath of config.manifestPaths) { - if (!(yield* fs.exists(manifestPath))) continue - - const manifest = JSON.parse(yield* fs.readFileString(manifestPath)) - - for (const section of depSections) { - const deps = (manifest[section] ?? {}) as Record - - for (const [name, range] of Object.entries(deps)) { - if (name !== "effect" && !name.startsWith("@effect/")) continue - - const normalized = normalizeVersion(String(range)) - if (!normalized) continue - - versionMentions.push({ manifestPath, section, name, range: String(range), normalized }) - } - } - } - - const effectPackageMentions = versionMentions.filter((e) => e.name === "effect") - - const versionsFromEffectPackage = [...new Set(effectPackageMentions.map((e) => e.normalized))] - - const versionsFromEffectScope = [ - ...new Set( - versionMentions - .filter((e) => e.name.startsWith("@effect/") && e.normalized.startsWith("4.")) - .map((e) => e.normalized) - ) - ] - - const versions = versionsFromEffectPackage.length > 0 - ? versionsFromEffectPackage - : versionsFromEffectScope - - if (versions.length === 0) { - return yield* Effect.fail( - new Error("Could not find any effect/@effect dependency versions in manifests") - ) - } - - if (versions.length > 1) { - const details = (effectPackageMentions.length > 0 ? effectPackageMentions : versionMentions) - .map((e) => `${e.manifestPath} (${e.section}) ${e.name}: ${e.range}`) - .join("\n") - - return yield* Effect.fail( - new Error(`Found multiple Effect versions. Please align first:\n${details}`) - ) - } - - const version = versions[0] - const candidateRefs = [ - `effect@${version}`, - `@effect/effect@${version}`, - `@effect/ai-anthropic@${version}` - ] - - const url = shellQuote(config.url) - - let ref: string | undefined - for (const candidate of candidateRefs) { - const tagResult = yield* runGetString( - `git ls-remote --exit-code --tags ${url} ${shellQuote(`refs/tags/${candidate}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(tagResult)) { - ref = candidate - break - } - } - - if (!ref) { - return yield* Effect.fail( - new Error(`No matching tag found for version ${version}. Tried: ${candidateRefs.join(", ")}`) - ) - } - - yield* Effect.logInfo(`Using effect version: ${version}`) - yield* Effect.logInfo(`Using subtree ref: ${ref}`) - yield* Effect.logInfo(`Using subtree url: ${config.url}`) - const syncRef = "refs/effa-sync/effect-selected" - yield* fetchRemoteRef(`refs/tags/${ref}`, syncRef) - yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) -}) diff --git a/packages/cli/src/sync-shared.ts b/packages/cli/src/sync-shared.ts deleted file mode 100644 index 978374a38..000000000 --- a/packages/cli/src/sync-shared.ts +++ /dev/null @@ -1,349 +0,0 @@ -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Path from "effect/Path" -import { RunCommandService } from "./os-command.js" - -/** - * Project-side lockfile shape (`.shared.json`). - * - * `artifacts` maps SOURCE path (inside the shared repo) to DEST path (inside the - * consuming project). `exclude` lists source-relative paths to skip during sync. - */ -export interface SharedLockfile { - readonly repo: string - readonly ref: string - readonly artifacts: Record - readonly exclude?: ReadonlyArray - readonly synced_at?: string -} - -const sanitizeRepoSlug = (repo: string) => repo.replace(/[^A-Za-z0-9_.-]+/g, "_") - -const repoCloneUrl = (repo: string) => { - if (repo.startsWith("http") || repo.startsWith("git@")) return repo - // "github.com/effect-app/shared" → "git@github.com:effect-app/shared.git" - const m = repo.match(/^github\.com\/(.+?)\/(.+?)$/) - if (m) return `git@github.com:${m[1]}/${m[2]}.git` - return repo -} - -const joinPosix = (a: string, b: string) => a.endsWith("/") ? a + b : a + "/" + b - -const readLockfile = Effect.fnUntraced(function*(lockfilePath: string) { - const fs = yield* FileSystem.FileSystem - if (!(yield* fs.exists(lockfilePath))) { - return yield* Effect.fail(new Error(`No ${lockfilePath} found in current directory.`)) - } - const content = yield* fs.readFileString(lockfilePath) - return { content, lockfile: JSON.parse(content) as SharedLockfile } -}) - -const ensureCache = Effect.fnUntraced(function*(lockfile: SharedLockfile) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetExitCode } = yield* RunCommandService - - const home = process.env["HOME"] ?? process.env["USERPROFILE"] - if (!home) return yield* Effect.fail(new Error("Cannot resolve home directory.")) - - const cacheRoot = path.join(home, ".cache", "effa", "shared") - const cachePath = path.join(cacheRoot, sanitizeRepoSlug(lockfile.repo)) - const cloneUrl = repoCloneUrl(lockfile.repo) - - if (!(yield* fs.exists(cachePath))) { - yield* runGetExitCode(`mkdir -p ${JSON.stringify(cacheRoot)}`) - yield* runGetExitCode(`git clone ${JSON.stringify(cloneUrl)} ${JSON.stringify(cachePath)}`) - } else { - yield* runGetExitCode("git fetch --all --tags --prune", cachePath) - } - - yield* runGetExitCode(`git checkout ${JSON.stringify(lockfile.ref)}`, cachePath) - - return cachePath -}) - -/** - * Resolve `requestedRef` (or the remote default branch HEAD if omitted) to a - * concrete commit sha in the fetched cache. Accepts branch names, tags, or - * shas; bare branch names are resolved against `origin/` first so the result - * is the latest remote commit, not a stale local ref. - */ -const resolveLatestSha = Effect.fnUntraced(function*(cachePath: string, requestedRef?: string) { - const { runGetExitCode, runGetString } = yield* RunCommandService - - const target = requestedRef - ?? (yield* runGetString("git rev-parse --abbrev-ref origin/HEAD", cachePath)).trim() - - const candidates = target.includes("/") ? [target] : [`origin/${target}`, target] - for (const candidate of candidates) { - const rev = JSON.stringify(`${candidate}^{commit}`) - const code = yield* runGetExitCode(`git rev-parse --verify --quiet ${rev}`, cachePath) - if (code === 0) { - return (yield* runGetString(`git rev-parse ${rev}`, cachePath)).trim() - } - } - - return yield* Effect.fail(new Error(`Cannot resolve ref: ${target}`)) -}) - -/** - * Walk artifact files. For each file in the artifact map (respecting excludes), - * yields `{ srcRel, srcAbs, destAbs }`. - */ -const walkArtifacts = Effect.fnUntraced(function*( - lockfile: SharedLockfile, - cachePath: string -) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetString } = yield* RunCommandService - - const excludeSet = new Set(lockfile.exclude ?? []) - const results: Array<{ srcRel: string; srcAbs: string; destAbs: string; excluded: boolean }> = [] - - for (const [srcRel, destRel] of Object.entries(lockfile.artifacts)) { - const srcAbs = path.join(cachePath, srcRel) - const destAbs = path.resolve(destRel) - - if (!(yield* fs.exists(srcAbs))) continue - - const stat = yield* fs.stat(srcAbs) - - if (stat.type === "File") { - results.push({ srcRel, srcAbs, destAbs, excluded: excludeSet.has(srcRel) }) - continue - } - - const fileList = yield* runGetString(`find ${JSON.stringify(srcAbs)} -type f`) - for (const fileAbs of fileList.split("\n").filter((l) => l.trim() !== "")) { - const relInArtifact = path.relative(srcAbs, fileAbs) - const srcRelFull = joinPosix(srcRel, relInArtifact) - const destFileAbs = path.join(destAbs, relInArtifact) - results.push({ - srcRel: srcRelFull, - srcAbs: fileAbs, - destAbs: destFileAbs, - excluded: excludeSet.has(srcRelFull) - }) - } - } - - return results -}) - -/** - * Pull artifacts from the shared repo into the consuming project according to - * `.shared.json`. Idempotent — running twice without changes is a no-op. - * - * MVP: overwrites destination files. Caller is expected to inspect `git status` - * after sync to review local changes. Conflict handling lands in a follow-up. - */ -export const syncShared = Effect.fnUntraced(function*( - opts: { lockfilePath?: string; update?: boolean; ref?: string | undefined } = {} -) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetExitCode } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { content: lockfileContent, lockfile: pinned } = yield* readLockfile(lockfilePath) - - yield* Effect.logInfo(`Syncing from ${pinned.repo} @ ${pinned.ref}`) - - const cachePath = yield* ensureCache(pinned) - yield* Effect.logInfo(`Cache: ${cachePath}`) - - let lockfile = pinned - if (opts.update) { - const newRef = yield* resolveLatestSha(cachePath, opts.ref) - if (newRef === pinned.ref) { - yield* Effect.logInfo(`Already at latest: ${newRef.slice(0, 12)}`) - } else { - yield* Effect.logInfo(`Updating ref ${pinned.ref.slice(0, 12)} → ${newRef.slice(0, 12)}`) - yield* runGetExitCode(`git checkout ${JSON.stringify(newRef)}`, cachePath) - } - lockfile = { ...pinned, ref: newRef } - } - - const files = yield* walkArtifacts(lockfile, cachePath) - - let copied = 0 - let skipped = 0 - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) { - yield* Effect.logInfo(` exclude ${srcRel}`) - skipped++ - continue - } - yield* runGetExitCode(`mkdir -p ${JSON.stringify(path.dirname(destAbs))}`) - yield* runGetExitCode(`cp ${JSON.stringify(srcAbs)} ${JSON.stringify(destAbs)}`) - copied++ - } - - const today = new Date().toISOString().slice(0, 10) - const trailingNewline = lockfileContent.endsWith("\n") ? "\n" : "" - const updated = { ...lockfile, synced_at: today } - yield* fs.writeFileString(lockfilePath, JSON.stringify(updated, null, 2) + trailingNewline) - - yield* Effect.logInfo(`Sync complete: ${copied} copied, ${skipped} excluded.`) - yield* Effect.logInfo("Review changes with: git status") -}) - -/** - * Compare project files to cache files. Reports each file as one of: - * M modified locally (project differs from cache) - * A added locally (project has file, cache does not — for tracked artifacts) - * D deleted locally (cache has file, project does not) - * E excluded (skipped by lockfile) - * - * Files that match cache exactly are not listed. - */ -export const syncDiff = Effect.fnUntraced(function*(opts: { lockfilePath?: string } = {}) { - const fs = yield* FileSystem.FileSystem - const { runGetString } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { lockfile } = yield* readLockfile(lockfilePath) - - yield* Effect.logInfo(`Diffing against ${lockfile.repo} @ ${lockfile.ref}`) - - const cachePath = yield* ensureCache(lockfile) - const files = yield* walkArtifacts(lockfile, cachePath) - - const changes: Array<{ kind: "M" | "D" | "E"; srcRel: string; destPath: string }> = [] - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) { - changes.push({ kind: "E", srcRel, destPath: destAbs }) - continue - } - - if (!(yield* fs.exists(destAbs))) { - changes.push({ kind: "D", srcRel, destPath: destAbs }) - continue - } - - const srcHash = (yield* runGetString(`sha256sum ${JSON.stringify(srcAbs)}`)).split(" ")[0] - const destHash = (yield* runGetString(`sha256sum ${JSON.stringify(destAbs)}`)).split(" ")[0] - - if (srcHash !== destHash) { - changes.push({ kind: "M", srcRel, destPath: destAbs }) - } - } - - if (changes.length === 0) { - yield* Effect.logInfo("In sync. No diff.") - return - } - - for (const { kind, srcRel, destPath } of changes) { - yield* Effect.logInfo(`${kind} ${srcRel} -> ${destPath}`) - } - yield* Effect.logInfo("") - yield* Effect.logInfo( - `Summary: ${changes.filter((c) => c.kind === "M").length} modified, ${ - changes - .filter((c) => c.kind === "D") - .length - } missing in project, ${changes.filter((c) => c.kind === "E").length} excluded.` - ) -}) - -/** - * Push locally-modified synced files back to the shared repo on a new branch. - * Optionally opens a PR via `gh pr create`. - * - * Workflow: - * 1. Ensure cache fresh at pinned ref. - * 2. Detect modified files via hash compare (project vs cache). - * 3. Create new branch in cache, copy modified files in, commit, push. - * 4. Optionally run `gh pr create` if `--pr` set. - */ -export const syncPush = Effect.fnUntraced(function*(opts: { - lockfilePath?: string | undefined - message?: string | undefined - branch?: string | undefined - pr?: boolean | undefined -} = {}) { - const { runGetExitCode, runGetString } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { lockfile } = yield* readLockfile(lockfilePath) - - const cachePath = yield* ensureCache(lockfile) - const files = yield* walkArtifacts(lockfile, cachePath) - - const fs = yield* FileSystem.FileSystem - const modified: Array<{ srcRel: string; srcAbs: string; destAbs: string }> = [] - const deleted: Array<{ srcRel: string; srcAbs: string }> = [] - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) continue - if (!(yield* fs.exists(destAbs))) { - deleted.push({ srcRel, srcAbs }) - continue - } - const srcHash = (yield* runGetString(`sha256sum ${JSON.stringify(srcAbs)}`)).split(" ")[0] - const destHash = (yield* runGetString(`sha256sum ${JSON.stringify(destAbs)}`)).split(" ")[0] - if (srcHash !== destHash) { - modified.push({ srcRel, srcAbs, destAbs }) - } - } - - if (modified.length === 0 && deleted.length === 0) { - yield* Effect.logInfo("No local modifications to push.") - return - } - - yield* Effect.logInfo(`Pushing ${modified.length} modified, ${deleted.length} deleted file(s):`) - for (const { srcRel } of modified) { - yield* Effect.logInfo(` M ${srcRel}`) - } - for (const { srcRel } of deleted) { - yield* Effect.logInfo(` D ${srcRel}`) - } - - const branch = opts.branch - ?? `sync/from-${sanitizeRepoSlug(process.cwd().split("/").pop() ?? "project")}-${ - new Date().toISOString().slice(0, 10) - }` - - const message = opts.message ?? "sync: propagate local edits" - - // Stash anything in cache (defensive), create branch from pinned ref. - yield* runGetExitCode(`git stash --include-untracked || true`, cachePath) - yield* runGetExitCode(`git checkout -B ${JSON.stringify(branch)} ${JSON.stringify(lockfile.ref)}`, cachePath) - - for (const { srcAbs, destAbs } of modified) { - yield* runGetExitCode(`cp ${JSON.stringify(destAbs)} ${JSON.stringify(srcAbs)}`) - } - for (const { srcAbs } of deleted) { - yield* runGetExitCode(`git rm -f ${JSON.stringify(srcAbs)}`, cachePath) - } - - yield* runGetExitCode(`git add -A`, cachePath) - yield* runGetExitCode( - `git -c commit.gpgsign=false commit -m ${JSON.stringify(message)}`, - cachePath - ) - yield* runGetExitCode(`git push -u origin ${JSON.stringify(branch)}`, cachePath) - - if (opts.pr) { - yield* runGetExitCode( - `gh pr create --title ${JSON.stringify(message)} --body ${ - JSON.stringify(`Propagated from project at ${process.cwd()}.\n\nFiles:\n${ - [ - ...modified.map((m) => `- M ${m.srcRel}`), - ...deleted.map((d) => `- D ${d.srcRel}`) - ] - .join("\n") - }`) - } --head ${JSON.stringify(branch)}`, - cachePath - ) - } - - yield* Effect.logInfo(`Pushed branch ${branch} to shared repo.`) - yield* Effect.logInfo(opts.pr ? "PR opened." : "Open a PR with: gh pr create (from the cache dir)") -}) diff --git a/packages/cli/src/update-packages.ts b/packages/cli/src/update-packages.ts deleted file mode 100644 index ae2c84386..000000000 --- a/packages/cli/src/update-packages.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const makeNcuUpdateCommand = ( - filter: string, - options?: { readonly recursive?: boolean } -) => `pnpm ${options?.recursive ? "-r " : ""}exec ncu -u --target semver --filter "${filter}"` diff --git a/packages/cli/test.gists.yaml b/packages/cli/test.gists.yaml deleted file mode 100644 index 41b4b992b..000000000 --- a/packages/cli/test.gists.yaml +++ /dev/null @@ -1,27 +0,0 @@ -gists: - # "cli-config-banana": - # company: "banana" # acts like a namespace - # description: "Package configuration and main source for Effect-App CLI" - # public: false - # files: - # - "package.json" - # # - "packages/cli/src/index.ts" - "cli-config-mela": - company: "mela" # acts like a namespace - description: "Package configuration and main source for Effect-App CLI" - public: false - files: - - "package.json" - - # "test-files": - # description: "Test YAML files for gist functionality" - # public: false - # files: - # - "gists.yaml" - -settings: - # GitHub token environment variable name - token_env: "GIST_GITHUB_TOKEN" - - # Base directory for files (relative to where the CLI is run) - base_directory: "." diff --git a/packages/cli/test/argv-patch.test.ts b/packages/cli/test/argv-patch.test.ts deleted file mode 100644 index 11c0ea30c..000000000 --- a/packages/cli/test/argv-patch.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { execFileSync } from "node:child_process" -import path from "node:path" -import url from "node:url" -import { describe, expect, it } from "vitest" -import { patchArgvForWrapCommands } from "../src/argv-patch.js" - -describe("patchArgvForWrapCommands", () => { - const make = (...args: Array) => ["node", "effect-app-cli", ...args] - - describe("joins wrap args into a single element", () => { - it("index-multi with tsc --build", () => { - const argv = make("index-multi", "tsc", "--build") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build")) - }) - - it("packagejson with tsc --build and tsconfig path", () => { - const argv = make("packagejson", "tsc", "--build", "./tsconfig.src.json") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "tsc --build ./tsconfig.src.json")) - }) - - it("packagejson-packages with pnpm check", () => { - const argv = make("packagejson-packages", "pnpm", "check") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson-packages", "pnpm check")) - }) - - it("single wrap arg stays as-is", () => { - const argv = make("index-multi", "tsc") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc")) - }) - - it("wrap args with multiple --flags", () => { - const argv = make("index-multi", "tsc", "--build", "--watch", "--verbose") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build --watch --verbose")) - }) - - it("wrap args with short flags", () => { - const argv = make("packagejson", "pnpm", "-r", "check") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "pnpm -r check")) - }) - - it("wrap args with --flag=value syntax", () => { - const argv = make("index-multi", "tsc", "--build=./tsconfig.json") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build=./tsconfig.json")) - }) - - it("wrap args with --flag=\"quoted value\" syntax", () => { - const argv = make("index-multi", "tsc", "--outDir=\"dist/build\"") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --outDir=\"dist/build\"")) - }) - - it("wrap args with mixed quoted and unquoted flags", () => { - const argv = make("packagejson", "cmd", "--x=\"abc\"", "--y", "plain") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "cmd --x=\"abc\" --y plain")) - }) - - it("wrap args with single-quoted value in flag", () => { - const argv = make("index-multi", "cmd", "--config='my config.json'") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "cmd --config='my config.json'")) - }) - - it("wrap args with spaces inside quoted flag value", () => { - const argv = make("packagejson-packages", "cmd", "--msg=\"hello world\"", "--verbose") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson-packages", "cmd --msg=\"hello world\" --verbose")) - }) - }) - - describe("does nothing for non-wrap subcommands", () => { - it("nuke with flags", () => { - const argv = make("nuke", "--dry-run") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("nuke", "--dry-run")) - }) - - it("gist with --config flag", () => { - const argv = make("gist", "--config", "gists.yaml") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("gist", "--config", "gists.yaml")) - }) - - it("ue", () => { - const argv = make("ue") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("ue")) - }) - - it("link with path arg", () => { - const argv = make("link", "../../effect-app/libs") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("link", "../../effect-app/libs")) - }) - - it("unrecognized command", () => { - const argv = make("unknown", "tsc", "--build") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("unknown", "tsc", "--build")) - }) - }) - - describe("edge cases", () => { - it("no subcommand", () => { - const argv = ["node", "effect-app-cli"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["node", "effect-app-cli"]) - }) - - it("subcommand with no trailing args", () => { - const argv = make("index-multi") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi")) - }) - - it("empty argv", () => { - const argv: Array = [] - patchArgvForWrapCommands(argv) - expect(argv).toEqual([]) - }) - - it("single element argv", () => { - const argv = ["node"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["node"]) - }) - - it("mutates the original array", () => { - const argv = make("index-multi", "tsc", "--build") - const ref = argv - patchArgvForWrapCommands(argv) - expect(ref).toBe(argv) - expect(ref).toEqual(make("index-multi", "tsc --build")) - }) - - it("different node/script paths", () => { - const argv = ["/usr/local/bin/node", "/home/user/.npm/bin/effa", "packagejson", "pnpm", "check"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["/usr/local/bin/node", "/home/user/.npm/bin/effa", "packagejson", "pnpm check"]) - }) - }) -}) - -describe.skip("e2e: CLI spawns wrap command correctly", () => { - const binPath = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "../bin.js") - - const run = (...args: Array) => - execFileSync("node", [binPath, ...args], { - encoding: "utf-8", - timeout: 10_000, - env: { ...process.env, NO_COLOR: "1" } - }) - - it("packagejson spawns 'echo hello' and captures output", () => { - const out = run("packagejson", "echo", "hello") - expect(out).toContain("Spawning child command: echo hello") - expect(out).toContain("hello") - }) - - it("packagejson spawns command with --flags without quoting", () => { - const out = run("packagejson", "echo", "--build-test", "extra") - expect(out).toContain("Spawning child command: echo --build-test extra") - expect(out).toContain("--build-test extra") - }) - - it("packagejson with single quoted arg still works", () => { - const out = run("packagejson", "echo from-quoted") - expect(out).toContain("Spawning child command: echo from-quoted") - expect(out).toContain("from-quoted") - }) - - it("propagates non-zero exit code", () => { - try { - run("packagejson", "exit 42") - expect.unreachable("should have thrown") - } catch (e: any) { - expect(e.status).toBe(42) - } - }) - - it("index-multi spawns wrap command with --flags", () => { - const out = run("index-multi", "echo", "--build", "done") - expect(out).toContain("Spawning child command: echo --build done") - expect(out).toContain("--build done") - }) - - it("packagejson spawns command with --flag=\"value\" syntax", () => { - const out = run("packagejson", "echo", "--x=\"abc\"", "--y") - expect(out).toContain("Spawning child command: echo --x=\"abc\" --y") - }) -}) diff --git a/packages/cli/test/gist-cache.test.ts b/packages/cli/test/gist-cache.test.ts deleted file mode 100644 index 902e71424..000000000 --- a/packages/cli/test/gist-cache.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Effect, Layer } from "effect" -import { describe, expect, it } from "vitest" -import { GHGistService } from "../src/gist.js" -import { RunCommandService } from "../src/os-command.js" - -describe("GHGistService.loadGistCache", () => { - it("loads the company cache from the configured Gist ID without discovery", async () => { - const commands: Array = [] - const runCommand = Layer.succeed(RunCommandService)({ - runGetExitCode: (command) => { - commands.push(command) - return Effect.die(`Unexpected command: ${command}`) - }, - runGetString: (command) => { - commands.push(command) - - switch (command) { - case "gh gist view configured-cache-id --files": - return Effect.succeed("effa-gist.cache\nmako.json\n") - case "gh gist view configured-cache-id -f \"mako.json\"": - return Effect.succeed("[{\"name\":\"mako\",\"id\":\"content-gist-id\"}]") - default: - return Effect.die(`Unexpected command: ${command}`) - } - } - }) - - const cache = await Effect.runPromise( - Effect - .gen(function*() { - const gh = yield* GHGistService - return yield* gh.loadGistCache("configured-cache-id", "mako") - }) - .pipe( - Effect.provide(GHGistService.DefaultWithoutDependencies), - Effect.provide(runCommand) - ) - ) - - expect(cache.gist_id).toBe("configured-cache-id") - expect(cache.company).toBe("mako") - expect(cache.entries.map(({ id, name }) => ({ id, name }))).toEqual([ - { id: "content-gist-id", name: "mako" } - ]) - expect(commands).toEqual([ - "gh gist view configured-cache-id --files", - "gh gist view configured-cache-id -f \"mako.json\"" - ]) - }) - - it("creates a missing company cache file once without retrying", async () => { - const commands: Array = [] - const runCommand = Layer.succeed(RunCommandService)({ - runGetExitCode: (command) => { - commands.push(command) - return Effect.die(`Unexpected command: ${command}`) - }, - runGetString: (command) => { - commands.push(command) - - switch (command) { - case "gh gist view configured-cache-id --files": - return Effect.succeed("effa-gist.cache\n") - case "echo \"[]\" | gh gist edit configured-cache-id -a mako.json -": - return Effect.succeed("") - default: - return Effect.die(`Unexpected command: ${command}`) - } - } - }) - - const cache = await Effect.runPromise( - Effect - .gen(function*() { - const gh = yield* GHGistService - return yield* gh.loadGistCache("configured-cache-id", "mako") - }) - .pipe( - Effect.provide(GHGistService.DefaultWithoutDependencies), - Effect.provide(runCommand) - ) - ) - - expect(cache.gist_id).toBe("configured-cache-id") - expect(cache.company).toBe("mako") - expect(cache.entries).toEqual([]) - expect(commands).toEqual([ - "gh gist view configured-cache-id --files", - "echo \"[]\" | gh gist edit configured-cache-id -a mako.json -" - ]) - }) -}) diff --git a/packages/cli/test/update-packages.test.ts b/packages/cli/test/update-packages.test.ts deleted file mode 100644 index 9cca50be2..000000000 --- a/packages/cli/test/update-packages.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe, expect, it } from "vitest" -import { makeNcuUpdateCommand } from "../src/update-packages.js" - -describe("makeNcuUpdateCommand", () => { - it("targets the highest version allowed by the existing semver range", () => { - expect(makeNcuUpdateCommand("@effect-app/*")).toBe( - "pnpm exec ncu -u --target semver --filter \"@effect-app/*\"" - ) - expect(makeNcuUpdateCommand("@effect/*", { recursive: true })).toBe( - "pnpm -r exec ncu -u --target semver --filter \"@effect/*\"" - ) - }) -}) diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json deleted file mode 100644 index 069b63731..000000000 --- a/packages/cli/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext" - ], - "moduleResolution": "Node16", - "outDir": "./dist", - "rootDir": "./src", - "tsBuildInfoFile": "./dist/.tsbuildinfo" - }, - "include": [ - "./src/**/*.ts" - ], - "references": [] -} diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts deleted file mode 100644 index 85903fa64..000000000 --- a/packages/cli/vitest.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig(makeConfig(__dirname)) diff --git a/packages/e2e/CHANGELOG.md b/packages/e2e/CHANGELOG.md deleted file mode 100644 index 1971ebe5a..000000000 --- a/packages/e2e/CHANGELOG.md +++ /dev/null @@ -1,455 +0,0 @@ -# @effect-app/e2e - -## 4.0.0-beta.332 - -### Patch Changes - -- Updated dependencies [6b57330] - - effect-app@4.0.0-beta.332 - - @effect-app/infra@4.0.0-beta.332 - - @effect-app/vue@4.0.0-beta.332 - -## 4.0.0-beta.331 - -### Patch Changes - -- Updated dependencies [65835a6] - - @effect-app/infra@4.0.0-beta.331 - - effect-app@4.0.0-beta.331 - - @effect-app/vue@4.0.0-beta.331 - -## 4.0.0-beta.330 - -### Patch Changes - -- Updated dependencies [48d9f36] - - effect-app@4.0.0-beta.330 - - @effect-app/infra@4.0.0-beta.330 - - @effect-app/vue@4.0.0-beta.330 - -## 4.0.0-beta.329 - -### Patch Changes - -- Updated dependencies [459697f] - - effect-app@4.0.0-beta.329 - - @effect-app/infra@4.0.0-beta.329 - - @effect-app/vue@4.0.0-beta.329 - -## 4.0.0-beta.328 - -### Patch Changes - -- Updated dependencies [3a8c710] - - effect-app@4.0.0-beta.328 - - @effect-app/infra@4.0.0-beta.328 - - @effect-app/vue@4.0.0-beta.328 - -## 4.0.0-beta.327 - -### Patch Changes - -- Updated dependencies [ebffba8] - - @effect-app/infra@4.0.0-beta.327 - - effect-app@4.0.0-beta.327 - - @effect-app/vue@4.0.0-beta.327 - -## 4.0.0-beta.326 - -### Patch Changes - -- Updated dependencies [2ebf8ae] - - effect-app@4.0.0-beta.326 - - @effect-app/infra@4.0.0-beta.326 - - @effect-app/vue@4.0.0-beta.326 - -## 4.0.0-beta.325 - -### Patch Changes - -- Updated dependencies [f233f3d] - - effect-app@4.0.0-beta.325 - - @effect-app/infra@4.0.0-beta.325 - - @effect-app/vue@4.0.0-beta.325 - -## 4.0.0-beta.324 - -### Patch Changes - -- Updated dependencies [a0075b8] -- Updated dependencies [a0075b8] - - effect-app@4.0.0-beta.324 - - @effect-app/infra@4.0.0-beta.324 - - @effect-app/vue@4.0.0-beta.324 - -## 4.0.0-beta.323 - -### Patch Changes - -- Updated dependencies [ab289d4] - - effect-app@4.0.0-beta.323 - - @effect-app/infra@4.0.0-beta.323 - - @effect-app/vue@4.0.0-beta.323 - -## 4.0.0-beta.322 - -### Patch Changes - -- Updated dependencies [2231ef8] - - effect-app@4.0.0-beta.322 - - @effect-app/infra@4.0.0-beta.322 - - @effect-app/vue@4.0.0-beta.322 - -## 4.0.0-beta.321 - -### Patch Changes - -- Updated dependencies [18bae5b] - - effect-app@4.0.0-beta.321 - - @effect-app/infra@4.0.0-beta.321 - - @effect-app/vue@4.0.0-beta.321 - -## 4.0.0-beta.320 - -### Patch Changes - -- effect-app@4.0.0-beta.320 -- @effect-app/infra@4.0.0-beta.320 -- @effect-app/vue@4.0.0-beta.320 - -## 4.0.0-beta.319 - -### Patch Changes - -- effect-app@4.0.0-beta.319 -- @effect-app/infra@4.0.0-beta.319 -- @effect-app/vue@4.0.0-beta.319 - -## 4.0.0-beta.318 - -### Patch Changes - -- Updated dependencies [8bd5bfe] - - effect-app@4.0.0-beta.318 - - @effect-app/infra@4.0.0-beta.318 - - @effect-app/vue@4.0.0-beta.318 - -## 4.0.0-beta.317 - -### Patch Changes - -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] - - @effect-app/infra@4.0.0-beta.317 - - effect-app@4.0.0-beta.317 - - @effect-app/vue@4.0.0-beta.317 - -## 4.0.0-beta.316 - -### Patch Changes - -- effect-app@4.0.0-beta.316 -- @effect-app/infra@4.0.0-beta.316 -- @effect-app/vue@4.0.0-beta.316 - -## 4.0.0-beta.315 - -### Patch Changes - -- effect-app@4.0.0-beta.315 -- @effect-app/infra@4.0.0-beta.315 -- @effect-app/vue@4.0.0-beta.315 - -## 4.0.0-beta.314 - -### Patch Changes - -- Updated dependencies [61a3931] - - @effect-app/vue@4.0.0-beta.314 - - effect-app@4.0.0-beta.314 - - @effect-app/infra@4.0.0-beta.314 - -## 4.0.0-beta.313 - -### Patch Changes - -- Updated dependencies [eddda2e] - - @effect-app/vue@4.0.0-beta.313 - - effect-app@4.0.0-beta.313 - - @effect-app/infra@4.0.0-beta.313 - -## 4.0.0-beta.312 - -### Patch Changes - -- Updated dependencies [9ea024d] - - effect-app@4.0.0-beta.312 - - @effect-app/infra@4.0.0-beta.312 - - @effect-app/vue@4.0.0-beta.312 - -## 4.0.0-beta.311 - -### Patch Changes - -- Updated dependencies [547e2e1] - - effect-app@4.0.0-beta.311 - - @effect-app/infra@4.0.0-beta.311 - - @effect-app/vue@4.0.0-beta.311 - -## 4.0.0-beta.310 - -### Patch Changes - -- Updated dependencies [0fa0e80] - - @effect-app/vue@4.0.0-beta.310 - - effect-app@4.0.0-beta.310 - - @effect-app/infra@4.0.0-beta.310 - -## 4.0.0-beta.309 - -### Patch Changes - -- Updated dependencies [99c43c4] -- Updated dependencies [99c43c4] - - effect-app@4.0.0-beta.309 - - @effect-app/vue@4.0.0-beta.309 - - @effect-app/infra@4.0.0-beta.309 - -## 4.0.0-beta.308 - -### Patch Changes - -- Updated dependencies [b4d5f55] - - @effect-app/infra@4.0.0-beta.308 - - effect-app@4.0.0-beta.308 - - @effect-app/vue@4.0.0-beta.308 - -## 4.0.0-beta.307 - -### Patch Changes - -- Updated dependencies [40585ca] -- Updated dependencies [ad0ede6] - - effect-app@4.0.0-beta.307 - - @effect-app/infra@4.0.0-beta.307 - - @effect-app/vue@4.0.0-beta.307 - -## 4.0.0-beta.306 - -### Patch Changes - -- Updated dependencies [0fe925d] - - effect-app@4.0.0-beta.306 - - @effect-app/infra@4.0.0-beta.306 - - @effect-app/vue@4.0.0-beta.306 - -## 4.0.0-beta.305 - -### Patch Changes - -- Updated dependencies [8bd9a11] - - effect-app@4.0.0-beta.305 - - @effect-app/infra@4.0.0-beta.305 - - @effect-app/vue@4.0.0-beta.305 - -## 4.0.0-beta.304 - -### Patch Changes - -- Updated dependencies [412f08b] - - effect-app@4.0.0-beta.304 - - @effect-app/infra@4.0.0-beta.304 - - @effect-app/vue@4.0.0-beta.304 - -## 4.0.0-beta.303 - -### Patch Changes - -- Updated dependencies [33b0544] - - effect-app@4.0.0-beta.303 - - @effect-app/infra@4.0.0-beta.303 - - @effect-app/vue@4.0.0-beta.303 - -## 4.0.0-beta.302 - -### Patch Changes - -- Updated dependencies [c0e5a1b] - - effect-app@4.0.0-beta.302 - - @effect-app/infra@4.0.0-beta.302 - - @effect-app/vue@4.0.0-beta.302 - -## 4.0.0-beta.301 - -### Patch Changes - -- Updated dependencies [413022d] - - @effect-app/vue@4.0.0-beta.301 - - effect-app@4.0.0-beta.301 - - @effect-app/infra@4.0.0-beta.301 - -## 4.0.0-beta.300 - -### Patch Changes - -- effect-app@4.0.0-beta.300 -- @effect-app/infra@4.0.0-beta.300 -- @effect-app/vue@4.0.0-beta.300 - -## 4.0.0-beta.299 - -### Patch Changes - -- effect-app@4.0.0-beta.299 -- @effect-app/infra@4.0.0-beta.299 -- @effect-app/vue@4.0.0-beta.299 - -## 4.0.0-beta.298 - -### Patch Changes - -- Updated dependencies [08d092c] - - effect-app@4.0.0-beta.298 - - @effect-app/vue@4.0.0-beta.298 - - @effect-app/infra@4.0.0-beta.298 - -## 4.0.0-beta.297 - -### Patch Changes - -- Updated dependencies [30c512d] - - effect-app@4.0.0-beta.297 - - @effect-app/infra@4.0.0-beta.297 - - @effect-app/vue@4.0.0-beta.297 - -## 4.0.0-beta.296 - -### Patch Changes - -- Updated dependencies [e0c4835] - - effect-app@4.0.0-beta.296 - - @effect-app/infra@4.0.0-beta.296 - - @effect-app/vue@4.0.0-beta.296 - -## 4.0.0-beta.295 - -### Patch Changes - -- Updated dependencies [eb06b32] - - effect-app@4.0.0-beta.295 - - @effect-app/infra@4.0.0-beta.295 - - @effect-app/vue@4.0.0-beta.295 - -## 4.0.0-beta.294 - -### Patch Changes - -- Updated dependencies [a211c12] -- Updated dependencies [7519318] - - effect-app@4.0.0-beta.294 - - @effect-app/vue@4.0.0-beta.294 - - @effect-app/infra@4.0.0-beta.294 - -## 4.0.0-beta.293 - -### Patch Changes - -- Updated dependencies [d1c15d3] - - effect-app@4.0.0-beta.293 - - @effect-app/infra@4.0.0-beta.293 - - @effect-app/vue@4.0.0-beta.293 - -## 4.0.0-beta.292 - -### Patch Changes - -- Updated dependencies [4622a75] - - @effect-app/vue@4.0.0-beta.292 - - effect-app@4.0.0-beta.292 - - @effect-app/infra@4.0.0-beta.292 - -## 4.0.0-beta.291 - -### Patch Changes - -- Updated dependencies [848d26d] -- Updated dependencies [fa3533e] - - @effect-app/infra@4.0.0-beta.291 - - @effect-app/vue@4.0.0-beta.291 - - effect-app@4.0.0-beta.291 - -## 4.0.0-beta.290 - -### Patch Changes - -- Updated dependencies [dc07df5] - - effect-app@4.0.0-beta.290 - - @effect-app/infra@4.0.0-beta.290 - - @effect-app/vue@4.0.0-beta.290 - -## 4.0.0-beta.289 - -### Patch Changes - -- Updated dependencies [a1b59bc] - - effect-app@4.0.0-beta.289 - - @effect-app/infra@4.0.0-beta.289 - - @effect-app/vue@4.0.0-beta.289 - -## 4.0.0-beta.288 - -### Patch Changes - -- Updated dependencies [aeb17bc] -- Updated dependencies [aeb17bc] - - effect-app@4.0.0-beta.288 - - @effect-app/vue@4.0.0-beta.288 - - @effect-app/infra@4.0.0-beta.288 - -## 4.0.0-beta.287 - -### Patch Changes - -- Updated dependencies [261470f] - - effect-app@4.0.0-beta.287 - - @effect-app/infra@4.0.0-beta.287 - - @effect-app/vue@4.0.0-beta.287 - -## 4.0.0-beta.286 - -### Patch Changes - -- Updated dependencies [b6dda09] - - effect-app@4.0.0-beta.286 - - @effect-app/infra@4.0.0-beta.286 - - @effect-app/vue@4.0.0-beta.286 - -## 4.0.0-beta.285 - -### Patch Changes - -- Updated dependencies [7bd8234] - - effect-app@4.0.0-beta.285 - - @effect-app/infra@4.0.0-beta.285 - - @effect-app/vue@4.0.0-beta.285 - -## 4.0.0-beta.284 - -### Patch Changes - -- Updated dependencies [dd239fa] - - @effect-app/infra@4.0.0-beta.284 - - @effect-app/vue@4.0.0-beta.284 - - effect-app@4.0.0-beta.284 - -## 4.0.0-beta.283 - -### Patch Changes - -- Updated dependencies [0054611] -- Updated dependencies [0054611] - - effect-app@4.0.0-beta.283 - - @effect-app/infra@4.0.0-beta.283 - - @effect-app/vue@4.0.0-beta.283 diff --git a/packages/e2e/package.json b/packages/e2e/package.json deleted file mode 100644 index c84733006..000000000 --- a/packages/e2e/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@effect-app/e2e", - "version": "4.0.0-beta.332", - "license": "MIT", - "type": "module", - "private": true, - "dependencies": { - "@effect-app/infra": "workspace:*", - "@effect-app/vue": "workspace:*", - "effect-app": "workspace:*" - }, - "devDependencies": { - "@effect/atom-vue": "^4.0.1", - "@effect/platform-node": "4.0.1", - "@effect/vitest": "4.0.1", - "@tanstack/vue-query": "5.96.2", - "@types/node": "25.9.1", - "@vitejs/plugin-vue": "^6.0.7", - "effect": "^4.0.1", - "typescript": "~6.0.3", - "vitest": "^4.1.7", - "vue": "^3.5.35" - }, - "scripts": { - "check": "tsgo --noEmit", - "test": "vitest", - "test:run": "vitest run --passWithNoTests", - "lint": "oxlint --quiet --type-aware ./test && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --quiet --type-aware --fix ./test && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "testsuite": "pnpm lint && pnpm run test:run", - "clean": "rm -rf dist" - }, - "sideEffects": false -} diff --git a/packages/e2e/test/repoInvalidation.e2e.test.ts b/packages/e2e/test/repoInvalidation.e2e.test.ts deleted file mode 100644 index f65566c6c..000000000 --- a/packages/e2e/test/repoInvalidation.e2e.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { awaitAtomResult, buildQueryFamily, invalidateAndAwait, makeAtomClientRuntime } from "@effect-app/vue/atomQuery" -import { invalidateQueries } from "@effect-app/vue/mutate" -import { defaultRegistry } from "@effect/atom-vue" -import { NodeHttpServer } from "@effect/platform-node" -import { expect, it } from "@effect/vitest" -import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query" -import { ApiClientFactory, InvalidStateError, makeRpcClient, NotLoggedInError, OptimisticConcurrencyException } from "effect-app/client" -import * as Context from "effect-app/Context" -import * as Effect from "effect-app/Effect" -import { tuple } from "effect-app/Function" -import { HttpRouter, HttpServer } from "effect-app/http" -import * as Layer from "effect-app/Layer" -import { DefaultGenericMiddlewares } from "effect-app/middleware" -import { makeRepo, RepositoryRegistryLive } from "effect-app/Model" -import { MiddlewareMaker, RpcContextMap, RpcMiddleware } from "effect-app/rpc" -import * as S from "effect-app/Schema" -import { FetchHttpClient } from "effect/http" -import * as ManagedRuntime from "effect/ManagedRuntime" -import * as NetAddress from "effect/net/NetAddress" -import * as Option from "effect/Option" -import * as Reactivity from "effect/reactivity/Reactivity" -import { RpcSerialization } from "effect/rpc" -import * as Scope from "effect/Scope" -import { createServer } from "http" -import { createApp, effectScope, ref } from "vue" -import { RequestContextMiddleware } from "../../infra/src/internal/RequestContextMiddleware.ts" -import { makeRouter } from "../../infra/src/routing.ts" -import { DefaultGenericMiddlewaresLive } from "../../infra/src/routing/middleware.ts" -import { MemoryStoreLive } from "../../infra/src/Store/Memory.ts" -import { makeTanstackQuery, makeTanstackQueryInvalidator } from "../../vue/src/internal/tanstackQuery.ts" - -class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError) -}) {} - -class AllowAnonymous extends RpcMiddleware.Tag()("AllowAnonymous", { - dynamic: RequestContextMap.get("allowAnonymous") -}) {} - -const AllowAnonymousLive = Layer.effect( - AllowAnonymous, - Effect.succeed(Effect.fnUntraced(function*(effect, { rpc }) { - yield* Scope.Scope - if (!RequestContextMap.getConfig(rpc).allowAnonymous) { - return yield* new NotLoggedInError({ message: "Not logged in" }) - } - return yield* effect - })) -) - -class AppMiddleware extends MiddlewareMaker - .Tag()("AppMiddleware", RequestContextMap) - .middleware(AllowAnonymous) - .middleware(...DefaultGenericMiddlewares) -{ - static Default = this.layer.pipe( - Layer.provide([AllowAnonymousLive, DefaultGenericMiddlewaresLive] as const) - ) -} - -const { Router, matchAll } = makeRouter(AppMiddleware.Default) -const { TaggedRequestFor } = makeRpcClient(AppMiddleware) -const Req = TaggedRequestFor("RepoInvalidation") - -class RepoItem extends S.Class("RepoItem")({ id: S.String, label: S.String }) {} -class OtherItem extends S.Class("OtherItem")({ id: S.String, label: S.String }) {} - -class RepoItems extends Context.Service()("RepoItems", { - make: makeRepo("RepoItem", RepoItem, {}) -}) { - static Default = Layer.effect(this, this.make).pipe( - Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive)) - ) -} - -class OtherItems extends Context.Service()("OtherItems", { - make: makeRepo("OtherItem", OtherItem, {}) -}) { - static Default = Layer.effect(this, this.make).pipe( - Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive)) - ) -} - -class GetRepoCount extends Req.Query()("GetRepoCount", {}, { - allowAnonymous: true, - success: S.Number -}) {} - -class GetRepoCountRuns extends Req.Query()("GetRepoCountRuns", {}, { - allowAnonymous: true, - success: S.Number -}) {} - -class SaveRepoItem extends Req.Command()("SaveRepoItem", { - id: S.String, - label: S.String -}, { - allowAnonymous: true, - error: S.Union([InvalidStateError, OptimisticConcurrencyException]), - success: S.Void -}) {} - -class SaveOtherItem extends Req.Command()("SaveOtherItem", { - id: S.String, - label: S.String -}, { - allowAnonymous: true, - success: S.Void -}) {} - -const RepoRpcs = { GetRepoCount, GetRepoCountRuns, SaveRepoItem, SaveOtherItem } -let repoCountRuns = 0 - -const router = Router(RepoRpcs)({ - dependencies: [RepoItems.Default, OtherItems.Default], - *effect(match) { - const repo = yield* RepoItems - const otherRepo = yield* OtherItems - return match({ - GetRepoCount: () => - Effect - .sync(() => repoCountRuns++) - .pipe(Effect.andThen(repo.all), Effect.map((items) => items.length), Effect.orDie), - GetRepoCountRuns: () => Effect.sync(() => repoCountRuns), - SaveRepoItem: ({ id, label }) => repo.save(new RepoItem({ id, label })).pipe(Effect.orDie), - SaveOtherItem: ({ id, label }) => otherRepo.save(new OtherItem({ id, label })).pipe(Effect.orDie) - }) - } -}) - -const RpcRouterLayer = matchAll({ router }) -const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 }) - -const ServerLayer = HttpRouter - .serve(RpcRouterLayer.pipe(Layer.provide(HttpRouter.middleware(RequestContextMiddleware()).layer))) - .pipe( - Layer.provide(NodeServerLayer), - Layer.provide(RpcSerialization.layerNdjson) - ) - -const ClientLayer = Layer - .unwrap( - Effect.gen(function*() { - const server = yield* HttpServer.HttpServer - const addr = server.address - if (NetAddress.isUnixPathAddress(addr)) { - return yield* Effect.die(new Error("expected inet address")) - } - const host = NetAddress.isUnspecified(addr.address) ? "127.0.0.1" : NetAddress.formatUrlHost(addr.address) - return ApiClientFactory - .layer({ url: `http://${host}:${addr.port}`, headers: Option.none() }) - .pipe(Layer.provide(FetchHttpClient.layer)) - }) - ) - .pipe(Layer.provide(NodeServerLayer)) - -const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer, Reactivity.layer) - -const setup = () => { - repoCountRuns = 0 - const runtime = ManagedRuntime.make(TestLayer) - return Effect - .all( - [ - ApiClientFactory.makeFor(Layer.empty)(RepoRpcs), - Effect.context() - ] as const - ) - .pipe(Effect.map(([client, context]) => tuple(runtime, client, context)), runtime.runPromise) -} - -it("atom engine: rpc repo write invalidates and refetches; unrelated rpc write does not", async () => { - const [runtime, client, context] = await setup() - const reactivity = Context.get(context, Reactivity.Reactivity) - const rt = makeAtomClientRuntime(() => Layer.succeedContext(context), runtime.memoMap) - const invalidator = { - invalidateAndAwait: (keys: ReadonlyArray>) => - invalidateAndAwait(keys).pipe(Effect.provideService(Reactivity.Reactivity, reactivity)) - } - const atom = buildQueryFamily(rt, client.GetRepoCount as any)(undefined) - const unmount = defaultRegistry.mount(atom) - const run = (effect: Effect.Effect) => Effect.runPromise(effect) - const save = (id: string) => - run( - invalidateQueries(client.SaveRepoItem, undefined, invalidator)(client.SaveRepoItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - const saveOther = (id: string) => - run( - invalidateQueries(client.SaveOtherItem, undefined, invalidator)(client.SaveOtherItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - - try { - expect(await run(awaitAtomResult(defaultRegistry, atom) as any)).toBe(0) - - await save("1") - expect(await run(awaitAtomResult(defaultRegistry, atom) as any)).toBe(1) - - const before = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - await saveOther("x") - const after = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - expect(after).toBe(before) - } finally { - unmount() - await runtime.dispose() - } -}, 10_000) - -it("tanstack engine: rpc repo write invalidates and refetches; unrelated rpc write does not", async () => { - const [runtime, client, context] = await setup() - const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false, gcTime: Infinity, staleTime: 0 } } - }) - const useQuery = makeTanstackQuery(() => context, queryClient) - const invalidator = makeTanstackQueryInvalidator(queryClient) - const app = createApp({ render: () => null }) - app.use(VueQueryPlugin, { queryClient }) - const scope = effectScope(true) - let handle: any - let data: any - const run = (effect: Effect.Effect) => Effect.runPromise(effect) - const save = (id: string) => - run( - invalidateQueries(client.SaveRepoItem, undefined, invalidator)(client.SaveRepoItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - const saveOther = (id: string) => - run( - invalidateQueries(client.SaveOtherItem, undefined, invalidator)(client.SaveOtherItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - - app.runWithContext(() => - scope.run(() => { - const tuple = useQuery(client.GetRepoCount as any)(ref(undefined) as any, {} as any) as any - data = tuple[1] - handle = tuple[3] - }) - ) - - try { - expect(await run(handle.refetch())).toBe(0) - - await save("1") - expect(data.value).toBe(1) - - const before = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - await saveOther("x") - const after = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - expect(after).toBe(before) - } finally { - scope.stop() - queryClient.clear() - await runtime.dispose() - } -}, 10_000) diff --git a/packages/e2e/tsconfig.json b/packages/e2e/tsconfig.json deleted file mode 100644 index d51e90c5f..000000000 --- a/packages/e2e/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext", - "DOM" - ], - "moduleResolution": "Node16", - "esModuleInterop": true, - "noEmit": true, - // Leaf test package: it imports workspace packages by source, so disable project-mode (which - // would demand every transitively-imported source file be listed) and emit nothing. - "composite": false, - "incremental": false, - "declarationMap": false, - "rootDir": "../..", - "types": [ - "node", - "vitest/globals" - ] - }, - "include": [ - "./test/**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/packages/e2e/vitest.config.ts b/packages/e2e/vitest.config.ts deleted file mode 100644 index 852ed8889..000000000 --- a/packages/e2e/vitest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig({ - ...makeConfig(__dirname), - test: { - environment: "node", - include: ["**/test/**/*.test.{ts,tsx}"], - exclude: ["node_modules/**", "dist/**"], - globals: true - } -}) diff --git a/packages/effect-app/.madgerc b/packages/effect-app/.madgerc deleted file mode 100644 index b407c6b4b..000000000 --- a/packages/effect-app/.madgerc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "detectiveOptions": { - "ts": { - "skipTypeImports": true - } - } -} diff --git a/packages/effect-app/.npmignore b/packages/effect-app/.npmignore deleted file mode 100644 index 9e4be5957..000000000 --- a/packages/effect-app/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -*.test.js -*.test.d.ts -tsconfig.build.json -*.log -tslint.json -.eslintrc.js -.eslintignore -.madgerc -.swcrc -.cache -coverage/ -.tsbuildinfo -.build/ -!dist/ \ No newline at end of file diff --git a/packages/effect-app/CHANGELOG.md b/packages/effect-app/CHANGELOG.md deleted file mode 100644 index 1e6263c70..000000000 --- a/packages/effect-app/CHANGELOG.md +++ /dev/null @@ -1,9105 +0,0 @@ -# @effect-app/prelude - -## 4.0.0-beta.332 - -### Patch Changes - -- 6b57330: Upgrade Effect packages to npm `4.0.0`. - -## 4.0.0-beta.331 - -## 4.0.0-beta.330 - -### Patch Changes - -- 48d9f36: Memory, SQLite, and Postgres `Store.queryRaw` now run over stored JSON documents, not Encoded rows. `Repository.queryRaw` already applies `toCodecJson` to the projector output, so native Encoded `Date` / `Map` / `Set` values no longer fail `Expected JSON value`. Cosmos was already JSON and is unchanged. - - Memory projectors that assumed native Encoded `Date` / `Map` / `Set` values now see the stored JSON form (ISO strings, arrays). - -## 4.0.0-beta.329 - -### Patch Changes - -- 459697f: Restore native Arbitrary for the schemas that lost custom fast-check `toArbitrary`. `StringId` uses `toCodecArbitrary` (the native replacement): generate a 210-byte `Uint8Array` and run `customRandom(urlAlphabet, 21, …)` — same as the old `StringIdArb`. JSON stays a branded string via `toCodec`. `Url` is `https://…`. `RequestId` samples unique nanoid-shaped ids. `Finite` generation is capped at ±1e6. `generateFromSchema` jumps its master seed per call so successive `count: 1` draws do not collide on attempt-0 edge strings. - -## 4.0.0-beta.328 - -### Minor Changes - -- 3a8c710: Store document decode fails as a typed `SchemaError` instead of a defect; repositories keep their public error channels by dying at the boundary; `validateSample` reports documents that fail at the store boundary. - -## 4.0.0-beta.327 - -## 4.0.0-beta.326 - -### Minor Changes - -- 2ebf8ae: `jitM` runs at the store boundary as JSON → JSON, before the `toCodecJson(toEncoded(schema))` decode; repositories decode the Encoded shape only. - - `jitM` used to be applied by the repository _after_ the store had already decoded the document, and was typed `(pm: Encoded) => Encoded` - a lie, since jitMs are written against the stored JSON and `Encoded` holds native `Date`/`Map`/`Set` values. - - The read pipeline is now: raw JSON document → merge `defaultValues` (unchanged: they only fill _absent_ keys, so an explicitly stored `null` reaches `jitM`) → `jitM` (JSON → JSON) → decode `Schema.toCodecJson(Schema.toEncoded(schema))` → Encoded. The repository then decodes Encoded → the domain type with `schema` and no longer applies `jitM` at all. Because `jitM` now runs before any schema decode sees the document, it can repair legacy shapes including explicit `null`s. - - - `StoreConfig.jitM?: (json: JsonRecord) => JsonRecord` is new, where `JsonRecord` (exported from `effect-app/Store`) is `{ readonly [key: string]: Schema.Json }`. It is not applied on the write/encode path, and never receives `_etag`. - - `RepositoryOptions.jitM` changes type from `(pm: Encoded) => Encoded` to `(json: JsonRecord) => JsonRecord` and is forwarded into the store config. - - **Migrating a jitM:** it now receives and must return JSON. A `Date` field arrives as an ISO string (return a string, not a `Date`), a `ReadonlySet` as an array, a `ReadonlyMap` as an array of `[key, value]` pairs. Field access is index-signature based (`json["x"]`) rather than typed property access. - - The JSON→Encoded decode stays strict: a document `jitM` does not repair fails at the store boundary instead of being read back half-decoded. - - `ValidationError.jitMResult` is deprecated: `validateSample` only ever sees the store's output, so it is identical to `rawData`. - -## 4.0.0-beta.325 - -### Patch Changes - -- f233f3d: `PositiveNumber` and `NonNegativeNumber` bound native Arbitrary generation to at most 1,000,000 (`arbitraryConstraint` on their existing checks), so generated fixtures no longer overflow derived totals to `Infinity`. Validation and JSON Schema output are unchanged. - -## 4.0.0-beta.324 - -### Patch Changes - -- a0075b8: `Email` and `PhoneNumber` carry native `arbitraryConstraint` patterns, so `effect/unstable/arbitrary` (and `generateFromSchema`) can sample them instead of exhausting rejection sampling on their `refine` guards. - -## 4.0.0-beta.323 - -### Minor Changes - -- ab289d4: Add opt-in `autoscaleMaxThroughput` for Cosmos containers created on demand (`StorageConfig`, `ClusterCosmosConfig`, `WorkflowEngineCosmosConfig`). When set, a missing container is created with autoscale at that max RU/s, unless its database has shared throughput (then it keeps sharing the database pool). Existing containers are untouched; unset keeps the Cosmos default of manual 400 RU/s per container. - -## 4.0.0-beta.322 - -### Patch Changes - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. - -## 4.0.0-beta.321 - -### Patch Changes - -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -## 4.0.0-beta.320 - -## 4.0.0-beta.319 - -## 4.0.0-beta.318 - -### Patch Changes - -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -## 4.0.0-beta.317 - -### Minor Changes - -- 186de3a: Stop forcing Date/Map/Set Encoded shapes to JSON. - - `Schema.Date` / `ReadonlySet` / `ReadonlyMap` now keep native Encoded types (`Date`, `Set`, `Map`). Use `DateFromString`, `ReadonlySetFromArray`, and `ReadonlyMapFromArray` when the Encoded form must be JSON. The query DSL accepts those native values, including array ops (`includes` / `in` / `includes-any`) on `Date[]` and `ReadonlySet` fields. Memory, Disk, SQL, and Cosmos convert Encoded Date/Map/Set through `Schema.toCodecJson` on write/read; query parameters and defaults lower the same way from the store schema. App types such as DateOnly stay native Encoded and JSON-lower via that schema — not a type registry. - -- 186de3a: Query maps as JSON arrays of `[key, value]` tuples. - - `where("meta", "hasKey" | "hasValue" | "hasKeyValue", ...)` (and `not*` / `*-any` / `*-all` variants) filter `ReadonlyMap` fields. Memory, Disk, SQLite, Postgres, and Cosmos compile those ops against the encoded tuple array. - -- 186de3a: JSON stores lower native Encoded values (Date, Map, Set, and app types such as DateOnly) through the store's document schema. - - `makeRepo` already passes that schema. Adapters encode documents, query parameters, and defaults with `Schema.toCodecJson(toEncoded(schema))` at the field path. No type registry. Schemaless stores still lower Date/Map/Set structurally. - -## 4.0.0-beta.316 - -## 4.0.0-beta.315 - -## 4.0.0-beta.314 - -## 4.0.0-beta.313 - -## 4.0.0-beta.312 - -### Patch Changes - -- 9ea024d: Improve repository schema encode/decode telemetry for event-loop tail analysis. - - - widen `app.schema.{encode,decode}.duration` histogram buckets into multi-second stalls - - count `app.schema.slow` (duration ≥ 100ms) with `app.entity` / operation attributes for alertable rates - - annotate spans with `app.schema.slow` and, on encode, `app.entity.state` from the first item's `_tag` - -## 4.0.0-beta.311 - -### Patch Changes - -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -## 4.0.0-beta.310 - -## 4.0.0-beta.309 - -### Patch Changes - -- 99c43c4: Derive scoped dependency reads and writes from typed query filters and annotated model relationships, including previous aliases. - -## 4.0.0-beta.308 - -## 4.0.0-beta.307 - -### Patch Changes - -- 40585ca: Keep tag-aware `ProjectableFromDomain` for `projectComputed` only; restore loose key-presence guard for `project()` so view DTOs with reshaped fields keep typechecking. -- ad0ede6: Keep tag-aware `ProjectableGuard` on both `project()` and `projectComputed` (no loose key-only paper-over). - - Hardening: - - - single-literal tags allow dual same-tag domain variants (KeysOfUnion of matched members) - - multi-tag / string tags still require keys on every matched member - - optional projection/domain keys checked by key presence only (not optional-assignability) - - Call sites must project domain-owned fields per tagged state. - -## 4.0.0-beta.306 - -### Patch Changes - -- 0fe925d: Tag-aware `ProjectableFromDomain` for `projectComputed`: projection Encoded fields must exist on the matching domain tagged state (or be computed). Prevents Overview.List SchemaErrors when cancel states omit workflow lock fields like `activeRequest`. - -## 4.0.0-beta.305 - -### Minor Changes - -- 8bd9a11: Support ID-scoped signal dependencies and additive repository dependencies derived from previous and current entities. - -## 4.0.0-beta.304 - -### Patch Changes - -- 412f08b: Include changed entity IDs in repository data dependencies, with explicit query scopes and configurable write aliases, while retaining coarse collection dependencies. - -## 4.0.0-beta.303 - -### Patch Changes - -- 33b0544: Replace repository codec child spans with parent-span timing attributes and bounded metrics, and record database operation timing for instrumented stores. - -## 4.0.0-beta.302 - -### Patch Changes - -- c0e5a1b: Add `Schema.dropConstructorDefault`, a `Struct.Lambda` that clears a field's `withConstructorDefault`. Compose it with `Struct.omit`/`Struct.map` when deriving a partial-update schema from a "create" schema, so omitted fields' constructor defaults don't resurrect themselves in `.make(...)` output. - - Also, `makeExactOptional` now drops constructor defaults automatically, matching this behavior. - -## 4.0.0-beta.301 - -## 4.0.0-beta.300 - -## 4.0.0-beta.299 - -## 4.0.0-beta.298 - -### Patch Changes - -- 08d092c: Update Atom query caches in memory and accumulate stream invalidations so live events do not cause repeated RPC refetches. - -## 4.0.0-beta.297 - -### Patch Changes - -- 30c512d: fix rpc middleware context type issue - -## 4.0.0-beta.296 - -### Patch Changes - -- e0c4835: revert bs - -## 4.0.0-beta.295 - -### Patch Changes - -- eb06b32: improve root union select - -## 4.0.0-beta.294 - -### Patch Changes - -- a211c12: Fix projection schema typing for union members and computed projections. - -## 4.0.0-beta.293 - -### Patch Changes - -- d1c15d3: Tighten projectComputed projection schema typing against encoded repo fields and computed output types. - -## 4.0.0-beta.292 - -## 4.0.0-beta.291 - -## 4.0.0-beta.290 - -### Patch Changes - -- dc07df5: Fix nested DTO subset projection inference for tagged unions. - -## 4.0.0-beta.289 - -### Patch Changes - -- a1b59bc: Allow query projections to use a repository's full union schema. - -## 4.0.0-beta.288 - -### Minor Changes - -- aeb17bc: Add `disableQueryInvalidation` flag to Command config for background saves. - - Set `disableQueryInvalidation: true` in a `Req.Command` config (3rd argument) - to suppress all client-side query invalidation for that command — client - `invalidatesQueries` callbacks, server-returned `metadata.invalidateQueries`, - and repository-derived write-dependency matching are all skipped. Use for - background saves (e.g. debounced auto-save) whose writes should not trigger - query refetches. - - - `InvalidationConfig` gains `disableQueryInvalidation?: boolean`. - - `RequestHandlerWithInput` gains `disableQueryInvalidation?: boolean`, - propagated from `Request.config` by `ApiClientFactory.makeFor`. - - `invalidateCache` early-returns `Effect.void` when the flag is set, - applying to both regular and stream mutations. - -## 4.0.0-beta.287 - -### Patch Changes - -- 261470f: Remove the local `Schema.Void` / `SchemaAST.Void` override. effect `4.0.0-beta.90` ships the TypeScript `void` parser semantics upstream (`SchemaAST.Void.getParser()` → `fromAnyToConst(undefined)`, the effect-smol PR #2475 behavior), so `S.Void` already accepts any present value and discards it to `undefined`. `Schema.Void` now re-uses effect's `S.Void` and `SchemaAST` is a plain re-export of `effect/SchemaAST`. The unused `Void_` alias is dropped. No behavior change. - -## 4.0.0-beta.286 - -### Patch Changes - -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.285 - -### Patch Changes - -- 7bd8234: Use set-backed data dependencies and provide request-scoped dependency services through a shared root-checked helper. - -## 4.0.0-beta.284 - -## 4.0.0-beta.283 - -### Minor Changes - -- 0054611: Derive query invalidation from repository read/write dependencies and propagate dependency metadata through RPC clients. - -### Patch Changes - -- 0054611: Stream RPC: drain read/write data-dependencies per emitted value chunk instead of sending the - cumulative set on every metadata chunk. Each metadata chunk now carries only the delta recorded - since the last chunk, the bucket is cleared for the next segment, and the terminal "done"/"error" - chunks drain the remainder. The emit condition also broadens to include non-empty reads, so stream - queries forward their read-dependencies mid-stream too. The client already accumulates deltas into - its per-call recorder, so no FE change is required. - -## 4.0.0-beta.282 - -### Minor Changes - -- b2df3fa: Align `Schema.Void` with TypeScript `void` return-value semantics (effect-smol PR #2475 / `b7d46ab`). - - `S.Void` now accepts **any present value** at runtime and discards it to `undefined`, while keeping the decoded/encoded type as `void` — matching a `void` return whose result callers never observe. This is implemented as an override of the AST node in a new `effect-app/SchemaAST` module (`export * from "effect/SchemaAST"` plus a `Void` subclass whose parser mirrors the PR's `fromAnyToConst(undefined)`), so there is a single canonical `Void` used everywhere, including RPC success schemas. - - - New `effect-app/SchemaAST` module; internal `SchemaAST` imports across the libs now route through it. - - Removed `ForceVoid` from `effect-app/client/makeClient` — use `S.Void` directly, which now carries this behaviour. - - `S.Void_` remains available as effect's original (`undefined`-only) Void. - -### Patch Changes - -- f150cf9: Remove obsolete queue machinery left unused after the move to cluster entities: the SB/SQLite/mem `QueueMaker` implementations (`QueueMaker/{SQLQueue,memQueue,sbqueue}.ts`), their `ServiceBus.ts`/`memQueue.ts` transports, and `RequestFiberSet.ts` (its `setRootParentSpan` helper is inlined into `MainFiberSet`). `effect-app/QueueMaker` keeps only `QueueMeta`; the `QueueBase` interface and empty `QueueMaker` ops object are dropped. -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.281 - -### Patch Changes - -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.280 - -### Patch Changes - -- 3eda52e: Fix `StructFacade` to extend effect-app's own `Struct`, not `effect/Schema`'s. - - `StructFacade` was built on `Omit, ...>` where `S` is `effect/Schema` (core). Scanner / consumer models are effect-app `S.Struct`, which is a distinct type from effect core's `Struct`, so a facade built on the core `Struct` is not assignable where an effect-app struct is expected — e.g. workflow payloads (`Workflow<…, Struct>`) and other effect-app `Struct`-shaped positions failed to type-check. Switch the `Omit` base to effect-app's `Struct` (the `Fields extends S.Struct.Fields` constraint stays effect core, matching effect-app's own `Struct`). - -## 4.0.0-beta.279 - -### Minor Changes - -- 5f9cd6a: Schema/Class facades: add `StructFacade` and carry `identifier` on the class facades. - - - Add `StructFacade` = `Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> & { pinned type-level members }`. A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` model faceted by the `.d.ts`-emit compiler is retyped to it: still a real `S.Struct` (so `Workflow.AnyStructSchema`, the `Struct` reconstruction, `Union`, `.fields.x` keep working) while `Type`/`Encoded`/make/services resolve to the named namespace interfaces. - - Add `readonly identifier: string` to `OpaqueClassFacade` and `OpaqueErrorFacadeClass` (an `S.Class` static lost from the bare emitted interface, which the compiler otherwise had to hand-emit per model). `OpaqueFacade` is intentionally left untouched — `S.Opaque` and requests build on `S.Bottom`, not `S.Class`, so they have no `identifier`. - -## 4.0.0-beta.278 - -### Patch Changes - -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. - -## 4.0.0-beta.277 - -## 4.0.0-beta.276 - -### Patch Changes - -- 32f05c8: Remove unused dependency declarations from package manifests. - -## 4.0.0-beta.275 - -## 4.0.0-beta.274 - -## 4.0.0-beta.273 - -### Patch Changes - -- 664e83d: Add Atom-native Vue query APIs, stream query pull atoms, and a TanStack-backed legacy query engine toggle. -- e4ff9a6: Fix the global `Array`/`ReadonlyArray.map` override breaking union-of-array receivers. The previous `this: NonEmptyArray`/`NonEmptyReadonlyArray` overload was selected-then-rejected on a union receiver (e.g. `(readonly A[] | readonly B[]).map(...)`), raising TS2684. Replaced with a single conditional-return signature (`this extends NonEmpty… ? NonEmpty : U[]`) that preserves NonEmpty refinement without a `this` parameter, so union-array `.map` calls type-check again. - -## 4.0.0-beta.272 - -### Patch Changes - -- 21ac90a: Unify generated model facades on `OpaqueFacade` and support class facades through encoded-key schema transforms. - -## 4.0.0-beta.271 - -### Patch Changes - -- 939bebc: Remove the local `Config.nested` / `ConfigProvider.nested` overrides. They worked around an effect v3 bug where the nested namespace segment bypassed the provider's `mapInput`/`constantCase` transform. effect `4.0.0-beta.84` fixes this upstream (its transformation-compose refactor threads the nested prefix through the provider's lookup transform), and the old overrides relied on internals (`Config.make`, `provider.get`/`mapInput`/`prefix`) that no longer exist. Both modules now re-export effect directly; behaviour for consumers (e.g. `Config.nested("cups")` against a `constantCase` env provider) is unchanged and correct. - -## 4.0.0-beta.270 - -### Patch Changes - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). - -## 4.0.0-beta.269 - -### Patch Changes - -- 50b022e: Harden model facades and add `OpaqueErrorFacadeClass`. - - - `OpaqueErrorFacadeClass`: facade `TaggedErrorClass`/`ErrorClass` while keeping - `Cause.YieldableError` on the constructed instance (so `yield* new Err()`, - `Effect.fail`, and `instanceof` keep working through the facade). - - `OpaqueFacadeInput` relaxed to require only the codec service channels, so - transformed schemas (`.pipe(encodeKeys/annotate/filter/...)`) can be facaded; - `fields`/`copy`/`mapFields` flow through `OpaqueFacadeStatics` when present, and - `to` is carried so models that compose via `X.to.fields` keep working. - - Dropped the wide `fields`/`mapFields` overrides on the facade interfaces so the - precise statics win (keeps `Q.project(X.mapFields(...))` typed). - - Codegen (`eslint-codegen-model`): resolver prefers the private `_X` over the - self-referential facade; converges static service types in one run; per-model - classification (facade Opaque models, leave `Class` standard in mixed files); - `Make` emitted as `type X = {...} | void` when the make-input is voidable; - `readonly`-prefixed array/tuple elements parenthesized; value self-references in - the moved `_X` body rewritten to `_X`; instance getters surface on `Self`. - -- 50b022e: Add generated opaque model facades that expose static Type, Encoded, Make, and Schema declarations without leaking the private struct schema type to downstream project references. - -## 4.0.0-beta.268 - -### Patch Changes - -- acfdc9e: Point development package exports at TypeScript sources while keeping published exports on compiled dist files. - -## 4.0.0-beta.267 - -### Minor Changes - -- 23a7167: Add a typed, serializable `DatabaseError { message, transient, cause }` for store adapter failures. - - Store adapters wrapped DB calls in bare `Effect.promise` / `.orDie`, so a transient infra failure (request timeout, throttle, 5xx, dropped socket) became a raw-`Error` defect — which `Effect.retry` can't retry and which breaks JSON encoding of a workflow exit cause ("Expected JSON value, got Error"). `DatabaseError` is now exposed on all `Store` and `Repository` method error channels (writes: `OptimisticConcurrencyException | DatabaseError`) and added to `SupportedErrors` so the api/client/FE treat it as a 500-class error. Each adapter wraps its db-call failures into `DatabaseError` with a `transient` flag (timeout/throttle/5xx ⇒ retryable); the `cause` serializes via `Schema.Defect`. Construction/seed/DDL paths stay `orDie`. - -## 4.0.0-beta.266 - -### Patch Changes - -- bbaa67e: Fix `TS2589: Type instantiation is excessively deep and possibly infinite` when querying models that embed `Schema.Defect()` (or any field encoded as `Json`). - - The query path type `Path` (used by `Q.where`/`Q.and`/`Q.order` to type dotted field paths) recursed through object types with no termination for self-referential ones. Under effect `4.0.0-beta.83`, `Schema.Defect()` encodes as `Json`, whose `JsonObject = { readonly [x: string]: Json }` index signature made `Path` descend forever, blowing past TypeScript's instantiation limit on any model with such a field (e.g. carrier-error states carrying `raw: Schema.Defect()`). - - `Path` now threads a `Seen` set of already-entered object types and stops before re-entering one, so self-referential types terminate (the field itself stays a valid leaf path; only the unbounded descent is cut). Finite models are unaffected. - -## 4.0.0-beta.265 - -### Patch Changes - -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -## 4.0.0-beta.264 - -## 4.0.0-beta.263 - -## 4.0.0-beta.262 - -### Patch Changes - -- 55c6572: update packages - -## 4.0.0-beta.261 - -## 4.0.0-beta.260 - -### Patch Changes - -- bogus - -## 4.0.0-beta.259 - -## 4.0.0-beta.258 - -## 4.0.0-beta.257 - -### Minor Changes - -- e71eb78: Tighten typing of query DSL builders so field/path parameters are constrained to actual field paths instead of bare `string`: - - - `relation(path)` now infers the relation's element type and constrains `distinctCount`/`sum`/`collect`/`collectDistinct`/`collectFields`/`collectDistinctFields` and the `unit` of `sumExprBy`/`sumExprNormalized` to `FieldPath`. - - `relation(path).expr` exposes a scope-bound math-expression builder so `expr.field(...)` inside `sumExpr`/`sumExprBy`/`sumExprNormalized` is typed against the relation element. - - Top-level `expr.field` accepts an optional generic for opt-in tightening (`expr.field("x")`). - - `aggregate(schema, build)` accepts a builder callback whose `agg` argument is bound to the source row inferred from the pipe — paths are checked without any explicit generic: `make().pipe(aggregate(schema, ($) => ({ city: $.field("address.city") })))`. Plain `AggregateMap` form still accepted. - - `projectComputed(schema, build, mode?)` accepts a builder callback whose `{ relation }` argument is bound to the source row inferred from the pipe: `make().pipe(projectComputed(out, ({ relation }) => ({ total: relation("items").sum("qty") })))`. Plain `ComputedProjectionMap` form still accepted. - - `agg()` factory remains as an escape hatch when the builder is built outside a pipe. - -## 4.0.0-beta.256 - -### Patch Changes - -- 347af48: Fix schema interface widening regression introduced in 4.0.0-beta.255 (changeset `olive-onions-lose`). - - The original refactor annotated branded schema constants with interfaces extending `S.Codec & WithDefaults<...>`. That widened the underlying `BrandedSchema<...>` chain and dropped phantom slots (`Iso`, `~type.make.in`, `~type.parameters`, `Rebuild`, etc.) that downstream combinators read. In consumer projects this surfaced as `DecodingServices` / `EncodingServices` leaking as `unknown` — for example `Q.project(schema, "project")` failing with `Type 'unknown' is not assignable to type 'never'`, and RPC handler / generator-return shape mismatches against `Effect`. - - Each `*Schema` interface now extends the concrete underlying chain (e.g. `BrandedSchema`) and adds a call signature for `withDefaultMake` (and an explicit `withConstructorDefault` field for the numeric and `StringId` schemas). The runtime values are unchanged and the type-display improvement is preserved. - - Affected: `NonEmptyString*`, `Min3String255`, `StringId`, `Url`, `PositiveInt`, `NonNegativeInt`, `Int`, `PositiveNumber`, `NonNegativeNumber`, and `brandedStringId`. - -## 4.0.0-beta.255 - -### Patch Changes - -- 52a31dd: Reduce schema type complexity by exposing explicit interfaces for branded string and number schemas. - - Each branded constant in `Schema/strings.ts`, `Schema/moreStrings.ts`, and `Schema/numbers.ts` now has a named interface (e.g. `NonEmptyStringSchema`, `StringIdSchema`, `PositiveIntSchema`, `UrlSchema`) annotating its `export const`. Mirrors the pattern used in `effect/Schema` itself — TypeScript reports the interface name instead of expanding the full pipe/brand/extension chain, which shrinks inferred types in consumer code and speeds up type display. - - No runtime or API surface changes; `brandedStringId` now returns `BrandedStringIdSchema` (same shape as before, just named). - -## 4.0.0-beta.254 - -### Patch Changes - -- 29a1e57: Repository `changeFeed` is now namespace-aware. Events carry the `storeId` namespace as a third tuple element (`ChangeFeedEvent = [items, op, namespace]`). `subscribe` accepts `options.namespace` to register a per-namespace handler; omitting it registers a wildcard handler that receives events from every namespace. Per-namespace handler buckets eliminate cross-namespace fan-out and avoid waking handlers for irrelevant tenants. - -## 4.0.0-beta.253 - -### Patch Changes - -- b90fa30: Repository `changeFeed` now broadcasts synchronously: `publish` awaits every subscribed handler before resolving. Replaces `PubSub.PubSub` with a `ChangeFeed` interface (`publish` + scoped `subscribe`). Handlers are auto-removed when the subscriber's scope closes, and the full handler set is cleared when the repository's scope closes. Repository construction now requires `Scope` — wire `makeRepo` through `Layer.scoped` / `Effect.scoped`. - -## 4.0.0-beta.252 - -### Patch Changes - -- a788432: fix circular - -## 4.0.0-beta.251 - -### Patch Changes - -- 1c858d3: fix request scope problems - -## 4.0.0-beta.250 - -### Patch Changes - -- 3053760: fix: restore context map container behavior - -## 4.0.0-beta.249 - -### Minor Changes - -- ba789a2: Move core service contracts and runtime-agnostic modules into `effect-app`, keep `infra` and `vue` focused on adapters, and drop the temporary `infra` compatibility re-export paths in favor of the new canonical imports. - - `@effect-app/infra` no longer re-exports moved core modules such as `./Model`, `./Emailer/service`, `./QueueMaker/service`, `./Store/service`, `./adapters/*`, or `./api/*` entrypoints. - -## 4.0.0-beta.248 - -### Patch Changes - -- 2a86a17: improve tsgo compat: avoid deferred `Schema.Type`/`Codec.Encoded`/`Codec.DecodingServices`/`Codec.EncodingServices` conditional helpers in generic positions where the type parameter is already constrained to `Schema.Top`. Index the property directly (`X["Type"]`, `X["Encoded"]`, `X["DecodingServices"]`, …) so tsgo doesn't leak `unknown` into `Effect` channels (notably `R`). - - Sites: `client/clientFor.ts` (`RequestHandlerFor`, `FinalTypeOf`, `ExtractResponse`, `ExtractEResponse`), `client/makeClient.ts` (`InputFromPayload`, `OutputFromSuccess`, `InvalidationConfigForCommand`, `TaggedRequestWithMeta` overloads), `rpc/MiddlewareMaker.ts` (`Errors`), `rpc/RpcMiddleware.ts` (`Failure`, `FailureContext`), `Schema/ext.ts` (`ReadonlySetFromArray`, `ReadonlyMapFromArray`), `infra/routing.ts` (`GetSuccessShape`, handlers, route matcher), `vue/makeClient.ts` (`MutationExt.project`, `MutationWithExtensions`, `QueryProjection`), `vue/routeParams.ts` (`parseRouteParams*`). - -## 4.0.0-beta.247 - -### Patch Changes - -- 01bab22: Work around tsgo failing to reduce `S.Codec.DecodingServices` (`X extends Top ? X["DecodingServices"] : never`) in generic positions, which left `unknown` and polluted the `R` channel of client handlers and RPC middleware failure context. Since the schemas involved are already constrained to `S.Top`, read `["DecodingServices"]` directly in `RequestHandlerFor`, `TagClass.FailureContext`, and the Vue `MutationExt` / `QueryProjection` types. - -## 4.0.0-beta.246 - -## 4.0.0-beta.245 - -## 4.0.0-beta.244 - -### Patch Changes - -- b8b9080: update packages - -## 4.0.0-beta.243 - -## 4.0.0-beta.242 - -### Patch Changes - -- da83c1f: Align `InvalidationEntry` (vue) with `InvalidateQueryInstruction` (effect-app). - - `InvalidateQueryInstruction` is now parametrized over `Filters` / `Options` - (defaulting to `Record` so the core stays framework-agnostic). - `@effect-app/vue` exposes `InvalidationEntry` as a narrowed alias substituting - `@tanstack/vue-query`'s `InvalidateQueryFilters` and `InvalidateOptions`. Single - source of truth for the union shape across both packages. - -- 21017d5: `InvalidationSet.add` accepts arrays and is exposed as a static shortcut. - - - Single item: `yield* InvalidationSet.add(UserRsc.GetMe)` - - Batch: `yield* InvalidationSet.add([UserRsc.GetMe, ["custom", "key"]])` - - Skips the `.use(_ => _.add(...))` boilerplate. - - Existing `InvalidationSet.use(_ => _.add(...))` form still works; the service - identity is preserved via `Object.assign` so `Effect.provideService` and - `.use` / `.useSync` continue to operate on the same `Context.Reference`. - -- 2495ace: `InvalidationSet.add` accepts RPC handler shorthand. - - Server-side handlers may now pass an RPC handler object directly to - `InvalidationSet.add`; its query key is derived via `makeQueryKey`. Raw - `InvalidationKey` arrays continue to work. - - ```ts - // before - yield * - Invalidation.InvalidationSet.use((_) => _.add(makeQueryKey(UserRsc.GetMe))); - - // after - yield * Invalidation.InvalidationSet.use((_) => _.add(UserRsc.GetMe)); - ``` - -## 4.0.0-beta.241 - -### Patch Changes - -- b035b1c: Isolate per-call `Effect.provide(layers)` / `Stream.provide(layers)` in the - RPC api client factory via `{ local: true }`. - - `layers` here is built per RPC invocation from a caller-supplied - `requestLevelLayers` plus a fresh `RequestName` layer. Today both pieces are - stateless, so no observable bug — but `Effect.provide(layer)` without - `local: true` resolves its `MemoMap` from the ambient fiber context, and on a - long-lived runtime (browser app, server fiber) any stateful layer slipped into - `requestLevelLayers` by a caller would be memoized and shared across every - subsequent RPC call. `{ local: true }` builds the layer fresh per call and - skips the ambient MemoMap entirely. - - Also documents the rule in `AGENTS.md` ("Per-request `Effect.provide(layer)` - must isolate its MemoMap") so the pattern is enforceable in code review and - caught early. Companion to the `provideOnRequestScope` MemoMap fix in - `@effect-app/infra`. - -## 4.0.0-beta.240 - -## 4.0.0-beta.239 - -## 4.0.0-beta.238 - -## 4.0.0-beta.237 - -## 4.0.0-beta.236 - -## 4.0.0-beta.235 - -### Patch Changes - -- ba4bdc3: improve: support .ts extension -- 6cfd83d: update effect to latest beta - -## 4.0.0-beta.234 - -## 4.0.0-beta.233 - -### Patch Changes - -- 57a1862: Update to effect 4.0.0-beta.67. Switch deps from `pkg.pr.new` snapshot back to npm beta tag. - -## 4.0.0-beta.232 - -### Patch Changes - -- 0263827: Fix `Date` / `DateValid` default helpers to pipe from their own schema. - - `withConstructorDefault` and `withDecodingDefaultType` on `DateValid` previously piped from `DateFromString`, dropping the `isDateValid()` check (and, after the recent identifier split, the `DateValid` annotations) from the resulting defaulted schema. Both `Date` and `DateValid` now use `extendM` so the helpers attach to the underlying schema (`DateFromString` / `DateValidFromString`). - -- 0263827: Distinguish `Date` and `DateValid` in JSON Schema output. - - - `Date` now emits identifier `DateOrInvalid` with description noting the value may be invalid. - - `DateValid` now emits its own annotated string (identifier `Date`) with description stating a valid ISO 8601 date is required. - -- 0263827: Update to effect `pkg.pr.new` snapshot at `a42ef66` (4.0.0-beta.66). Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). - -## 4.0.0-beta.231 - -### Patch Changes - -- 7ca66ce: Update to effect 4.0.0-beta.66. Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). - -## 4.0.0-beta.230 - -## 4.0.0-beta.229 - -## 4.0.0-beta.228 - -## 4.0.0-beta.227 - -## 4.0.0-beta.226 - -## 4.0.0-beta.225 - -## 4.0.0-beta.224 - -## 4.0.0-beta.223 - -## 4.0.0-beta.222 - -## 4.0.0-beta.221 - -### Patch Changes - -- 439cbeb: Adopt module system from effect-smol: replace barrel imports with specific submodule imports (`import * as X from "effect-app/X"` / `import * as X from "effect/X"`). - -## 4.0.0-beta.220 - -## 4.0.0-beta.219 - -### Patch Changes - -- d738811: remove double prefix - -## 4.0.0-beta.218 - -## 4.0.0-beta.217 - -### Patch Changes - -- 0e824ef: add missing Brand - -## 4.0.0-beta.216 - -## 4.0.0-beta.215 - -## 4.0.0-beta.214 - -## 4.0.0-beta.213 - -## 4.0.0-beta.212 - -### Patch Changes - -- 8792221: Rename `withDefault` schema extensions to `withConstructorDefault` for clarity. - - Document that `.withConstructorDefault` is **construction-only** (applied during `.make(...)` when a field is omitted) and is **not** applied during decode — it cannot be used to JIT-migrate database fields. Per-property JSDoc on every `.withConstructorDefault` / `.withDecodingDefaultType` exposed by `Schema/ext.ts`, `Schema/numbers.ts`, `Schema/moreStrings.ts`, and `ids.ts` so the caveat is visible on hover. - - Re-export `withConstructorDefault`, `withDecodingDefault`, `withDecodingDefaultKey`, `withDecodingDefaultType`, and `withDecodingDefaultTypeKey` from `effect-app/Schema` with explicit JSDoc. `withDecodingDefault*` is discouraged for persisted data: a missing field may be data corruption rather than an old-shape document, and silently substituting a default hides the problem. Prefer an explicit, preferably versioned migration of database data over decode-time fallbacks. - -## 4.0.0-beta.211 - -## 4.0.0-beta.210 - -## 4.0.0-beta.209 - -### Patch Changes - -- c991be1: update packages -- 3bae238: Remove lodash type imports from utils.ts, replacing with native TypeScript equivalents. - -## 4.0.0-beta.208 - -## 4.0.0-beta.207 - -### Patch Changes - -- 8fffc3c: cleanup - -## 4.0.0-beta.206 - -### Patch Changes - -- 54bfc59: Require middleware to flow through `makeRpcClient` and the live layer through `makeRouter`. - - ### `makeRpcClient(middleware, generalErrors?)` - - Signature drops the `rcs` (request-context map wrapper) parameter. `rcs` was only load-bearing on the type side for `RequestConfig` inference; that information is now derived from `middleware.requestContextMap`. `middleware` is required — the previous "rcs + optional middleware" overload is gone. - - **Migration**: - - ```diff - -makeRpcClient(RequestContextMap, undefined, AppMiddleware) - +makeRpcClient(AppMiddleware) - ``` - - For tests/clients without a real middleware, build a minimal stub (`{ requestContextMap, requestContext }`) or pass any value satisfying `ClientMiddleware`. - - ### `makeRouter(middlewareLive)` - - `makeRouter()` no longer infers the live middleware layer from `meta.middleware.Default`. The Live layer is now passed explicitly to `makeRouter`, and the request classes only carry the middleware tag (schema-only). This decouples the router from any assumption that the middleware tag exposes a `Default` static. - - **Migration**: - - ```diff - -export const { Router, matchAll } = makeRouter() - +export const { Router, matchAll } = makeRouter(AppMiddleware.Default) - ``` - -## 4.0.0-beta.205 - -### Patch Changes - -- f313973: fix type make interface - -## 4.0.0-beta.204 - -### Patch Changes - -- 0a0030f: fix missing overloads - -## 4.0.0-beta.203 - -### Patch Changes - -- 992d9fa: fix more branded types - -## 4.0.0-beta.202 - -### Patch Changes - -- 1186b09: improve branded types, keep types through Rebuild - -## 4.0.0-beta.201 - -### Patch Changes - -- d67d17a: Source middleware errors exclusively from the rpc middleware tag, and move command/stream invalidation wrap/unwrap entirely into the routing layer (server) and `apiClientFactory` (client). `InvalidationMiddleware` and `InvalidationMiddlewareLive` are removed. - - ### Resource error schemas - - Three sites that used to fold `RequestContextMap[*].error` into a request's own error schema now stop doing so: - - - `makeRpcClient` / `makeRequestClass` — `failureSchema` is just `config.error` (still merged with the optional `generalErrors` parameter, which is the only remaining error mix on both type and runtime levels). - - `MiddlewareMaker.rpc()` — `error: options.error` only; the previous union with `rcm.config[*].error` is gone. - - Routing and `apiClientFactory.makeRpcGroupFromRequestsAndModuleName` — `Invalidation.makeCommandRpc` is called with `error: resource.error` (no widening with the composite middleware error union). - - Middleware errors reach the client through the rpc's `middlewares[*].error` failure-union channel of `Rpc.exitSchema`, exposed by attaching the middleware tag to the rpc on both sides: - - - **Server**: `makeRouter(middleware)` attaches the live composite tag (existing behavior). - - **Client**: new `middleware` option on `ClientForOptions` / `ApiClientFactory.makeFor(layer, { middleware })` attaches the same tag schema-only (no Live invoked). Threaded through `makeRpcGroupFromRequestsAndModuleName` to `RpcGroup.middleware(tag)`. Without it, stream rpcs (whose top-level `errorSchema` is forced to `Never` by effect-rpc) hit `SchemaError: Expected never | { _tag: "error", ... }` decoding middleware-thrown errors that bypass the in-stream `Stream.catch` wrap. - - **Migration**: handlers that yield errors previously sourced from rcm (e.g. `yield* new UnauthorizedError()`) now require those errors to be declared explicitly on the resource — `Req.Query()("...", fields, { success, error: UnauthorizedError })`. The handler error type no longer auto-includes the rcm union. - - ### Invalidation wrap/unwrap - - - `routing.ts` (server) provides a per-request `InvalidationSet` for commands, wraps the success value as `CommandResponseWithMetaData`, and converts handler-thrown failures into `CommandFailureWithMetaData` so accumulated invalidation keys reach the client on either path. Stream wrap (per-chunk envelope + final `done` chunk) was already in routing and is unchanged. - - `apiClientFactory.ts` (client) `unwrapCommand` strips both envelopes and forwards keys to `InvalidationKeysFromServer`. - - `InvalidationMiddleware` (the tag) and `InvalidationMiddlewareLive` (the layer) are **removed**. The middleware was the previous home of the wrap; with the wrap moved to routing/apiClientFactory, the middleware became a thin pass-through and is no longer needed. `DefaultGenericMiddlewares` and `DefaultGenericMiddlewaresLive` shrink accordingly — no migration needed for callers that used the defaults; callers that referenced `InvalidationMiddleware` / `InvalidationMiddlewareLive` directly should drop those imports. - - Middleware-thrown errors are never wrapped: by definition the handler never ran, so there is nothing to invalidate. They flow raw on the Cause and the client decodes them via the middleware-tag failure-union channel described above. - -## 4.0.0-beta.200 - -### Patch Changes - -- 8f1cf6a: able to configure schema concurrency -- 0cff7c1: workaround middleware error issue - -## 4.0.0-beta.199 - -## 4.0.0-beta.198 - -### Patch Changes - -- 32dbc54: fix stream type when no success specified - -## 4.0.0-beta.197 - -### Minor Changes - -- 3dc0d2a: Add streaming as a `stream: true` config option on `Query` / `Command` instead of a separate request type. - - `TaggedRequestFor` now exposes only `Query` and `Command` factories — the standalone `Stream` factory is removed. To produce a Stream of `success` values, pass `stream: true` in the request config. The request `type` field stays `"command" | "query"`; a new `stream: boolean` field carries the streaming flag (stripped from the stored handler config). - - ```ts - // Query that streams results - Req.Query()("Tag", {}, { stream: true, success: ... }) - - // Command that streams results - Req.Command()("Tag", {}, { stream: true, success: ... }) - ``` - - Vue client mapping (per-handler properties mirror the non-stream API — `.query`, `.fn`, `.mutate`): - - - `query` + `stream: true` → exposes `.query` (read-only streaming, tracked Vue Query). Helper map key: `${name}Query`. - - `command` + `stream: true` → exposes `.fn` and `.mutate` (mutating streaming). - - Plain `query` / `command` unchanged. - - Server routing dispatches via the new `stream` flag (`makeStreamRpc` for streaming commands/queries, `makeCommandRpc` / `Rpc.make` otherwise). - - Also lifts the `Struct` / `TaggedStruct` and `Opaque` definitions in `effect-app/Schema` to use `S.Bottom` / `S.Opaque` directly, exposing `fields`, `mapFields`, and a `MakeIn` that allows `void` when all fields are optional. `TaggedRequestFor` request classes now use `Opaque(TaggedStruct(...))` instead of `TaggedClass`, and decoding/encoding services are derived from `success` / `error` rather than stored on the request. - - **Migration**: replace `Req.Stream` with `Req.Query` or `Req.Command` and add `stream: true` to the config — `Query` for read-only streams, `Command` for mutating streams. - -## 4.0.0-beta.196 - -## 4.0.0-beta.195 - -### Patch Changes - -- 774a9b3: `MiddlewareMaker.makeMiddlewareBasic` now derives each middleware's effective error from both the static `error` field on the tag AND the `rcm` config entry referenced by `dynamic.key`, rather than relying on the static field alone. - - Middlewares declared with `dynamic: RequestContextMap.get("foo")` (instead of an explicit static `error: ...`) end up with `tag.error = Schema.Never` at runtime — `RpcMiddleware.Tag` defaults the static error to `Never` when not provided. The composite `MiddlewareMaker.Tag(...).middleware(...)` walked `make[*].error` to build its own error union, collapsing to `Union ≡ Never`. - - `Rpc.exitSchema` walks `rpc.middlewares[*].error` when building the wire-level failure union for every rpc kind. Empty-union meant middleware-thrown errors (`NotLoggedInError`, `UnauthorizedError`, etc.) never reached the wire schema. Query/command happened to work because their wire `errorSchema = resource.error` already covered the merge from `makeRpcClient`. Stream rpcs have `errorSchema` force-set to `Schema.Never` by effect-rpc, so the resource-level merge never reached the wire — middleware errors decoded as "Expected never, got X". - - Per middleware, the new logic pushes both the static `_.error` (if non-`Never`) and `rcm[_.dynamic.key].error` (if non-`Never`) into the composite's failure union. - -## 4.0.0-beta.194 - -## 4.0.0-beta.193 - -## 4.0.0-beta.192 - -## 4.0.0-beta.191 - -### Patch Changes - -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. - -## 4.0.0-beta.190 - -### Patch Changes - -- 985176b: Align request handler input typing with the request's `make` signature. Handlers are now classified as no-input only when the request schema declares no payload fields; any payload (even fully-optional) yields a function handler whose input matches `make`'s first parameter. Adds `HandlerInput` and threads it through `CommandFromRequest`. - -## 4.0.0-beta.189 - -### Patch Changes - -- ea32222: Update to effect 4.0.0-beta.60 and use native `Rpc.custom` constructors (`makeCommandRpc`, `makeStreamRpc`) for metadata-wrapped RPC schemas instead of manually wrapping/unwrapping schemas inline. - -## 4.0.0-beta.188 - -### Patch Changes - -- b2e438f: Remove Operations service and repo - -## 4.0.0-beta.187 - -### Patch Changes - -- 0d4e0b8: Fix `isGeneratorFunction` using `isObject` instead of `isFunction`: generator functions have `typeof === "function"`, not `"object"`, so the check always returned `false`. This caused `Command.streamFn` generator-form handlers to silently pass a raw `Generator` object rather than an `Effect`, meaning the mutation was never executed. - -## 4.0.0-beta.186 - -## 4.0.0-beta.185 - -## 4.0.0-beta.184 - -## 4.0.0-beta.183 - -## 4.0.0-beta.182 - -## 4.0.0-beta.181 - -### Patch Changes - -- 583393f: Default the stream `mutateStream` execute resolved value to the request's success type when no `final` schema is declared. - - Previously the type defaulted to `void`, but the runtime already resolves with the last emitted value. Types now match runtime behaviour: `execute` returns `Final` if a `final` schema is set, otherwise the success type. - -## 4.0.0-beta.180 - -### Minor Changes - -- 7fa3045: V1/V2/V3: stream and command requests carry invalidation metadata - - **V1** – stream final response includes metadata - - - `Invalidation.StreamResponseChunk` wraps each stream item as `{ _tag: "value", value }` and appends `{ _tag: "done", metadata }` at the end carrying all accumulated invalidation keys. - - **V2** – invalidation keys included in failures - - - `Invalidation.CommandFailureWithMetaData` and `Invalidation.StreamFailureChunk` carry keys accumulated up to the point of failure, so clients can invalidate queries even when a command or stream errors. - - `InvalidationMiddlewareLive` wraps command failures; `routing.ts` wraps stream failures. - - `apiClientFactory.ts` unwraps both on the client side, forwarding keys before re-failing with the original error. - - **V3** – mid-stream metadata chunks - - - `Invalidation.StreamResponseChunk` now also includes `{ _tag: "metadata", metadata }` for mid-stream invalidation. - - After each emitted value, the server drains accumulated keys and emits a "metadata" chunk if any keys were collected since the last drain (bucket reset via `InvalidationSet.drain`). - - `apiClientFactory.ts` processes "metadata" chunks the same as "done" chunks, forwarding keys to `InvalidationKeysFromServer` immediately. - - `makeInvalidationKeysService` accepts an optional `onAdded` callback that fires after each key addition, enabling `mutate.ts` to trigger query invalidation mid-stream without waiting for the stream to complete. - -## 4.0.0-beta.179 - -### Minor Changes - -- 828d264: Stream requests now support an optional `final` schema that models the final success type of the stream. When declared, `mutateStream`'s execute effect resolves with the last emitted value typed as `Final` instead of `void`. - - ```ts - class MyStream extends SomethingStream()( - "MyStream", - { id: S.String }, - { - success: S.Union([OperationProgress, ExportComplete]), - final: ExportComplete, // execute now resolves with ExportComplete - } - ) {} - ``` - -## 4.0.0-beta.178 - -## 4.0.0-beta.177 - -### Minor Changes - -- 89d8b3a: Add Effect RPC `Stream` support to the wrapper. - - - New `Stream` request constructor on `TaggedRequestFor` parallel to `Query`/`Command`. Emits resources with `type: "stream"`. - - Server router (`@effect-app/infra` `routing.ts`) accepts stream resources whose handlers return a `Stream.Stream` (or a function from input to one). Forwards `stream: true` to `Rpc.make` so `RpcSchema.Stream` wrapping is applied. Streams bypass `applyRequestTypeInterruptibility` and the `Effect.withSpan` wrapping (the RPC server adds its own span). - - Client (`apiClientFactory.ts`) detects stream resources, forwards `stream: true` when constructing `RpcGroup`, and exposes the per-request `handler` as a `Stream.Stream` (via `Stream.unwrap` over the `ManagedRuntime` context) instead of an `Effect`. `Invalidation.CommandResponseWithMetaData` continues to apply only to commands. - - New `RequestStreamHandler` / `RequestStreamHandlerWithInput` shapes in `clientFor.ts`; `RequestHandlers` dispatches on `type: "stream"`. - -## 4.0.0-beta.176 - -### Patch Changes - -- pass options - -## 4.0.0-beta.175 - -## 4.0.0-beta.174 - -### Minor Changes - -- 821468d: Add server-driven cache invalidation via RPC response headers. - - - `effect-app/rpc`: new `Invalidation` module with `InvalidationKey` / `InvalidationKeys` schemas, `Invalidates` annotation (for declaring static invalidation on Rpc definitions), `InvalidationSet` reference (request-scoped accumulator), and `makeInvalidationSet` helper. - - `effect-app/middleware`: new `InvalidationMiddleware` RPC middleware tag; included in `DefaultGenericMiddlewares`. - - `effect-app/client`: new `InvalidationKeys` module with `InvalidationKeysFromServer` reference and `makeInvalidationKeysService` helper; `apiClientFactory` now taps HTTP responses to read the `x-invalidate` header and forward keys to `InvalidationKeysFromServer`. - - `@effect-app/infra`: new `InvalidationMiddlewareLive` RPC middleware implementation that owns the full lifecycle — creates a request-scoped `InvalidationSet` (backed by a `Ref`), pre-populates it from the `Invalidates` annotation, provides it to the handler, and after the handler completes registers an HTTP pre-response handler (via `appendPreResponseHandlerUnsafe`) to write the accumulated keys as an `x-invalidate` response header. No separate HTTP middleware is needed. - - `@effect-app/vue`: `invalidateQueries` / `useMutation` now reads server-provided invalidation keys from `InvalidationKeysFromServer` after each mutation and applies them alongside the client-side invalidation. - -## 4.0.0-beta.173 - -## 4.0.0-beta.172 - -## 4.0.0-beta.171 - -### Patch Changes - -- d71d976: fix - -## 4.0.0-beta.170 - -### Patch Changes - -- 8f09f77: fix - -## 4.0.0-beta.169 - -### Patch Changes - -- 8ae8b53: input mess - -## 4.0.0-beta.168 - -### Patch Changes - -- 178480a: Fix request handler input classification to use request schema fields instead of `make` parameters, preventing defaulted/nullable input fields from being treated as no-input handlers. - -## 4.0.0-beta.167 - -### Patch Changes - -- 140e192: Relax invalidation resource value constraints to allow arbitrary values while preserving query-only filtering in invalidation handling. - -## 4.0.0-beta.166 - -### Patch Changes - -- dbcc53b: Refactor command invalidation typing: declare resources via `Command()`, pass `invalidatesQueries` as the optional 4th argument, and enforce exact `clientFor` invalidation resources when required. - -## 4.0.0-beta.165 - -### Minor Changes - -- f88ea34: Move `makeQueryKey` into `effect-app/client` and update Vue source and tests to import it from the shared client module. Vue still re-exports `makeQueryKey` from `src/lib` for compatibility. - -## 4.0.0-beta.164 - -### Minor Changes - -- 8cb3de4: Add command invalidation helpers that preserve query-only resource types and pass mutation input and `Exit` results into invalidation callbacks. Update Vue `clientFor` to merge request-level invalidation config with call-site invalidation and require matching invalidation resources. - -## 4.0.0-beta.163 - -### Patch Changes - -- b952f19: bye cruft - -## 4.0.0-beta.162 - -### Patch Changes - -- b52b424: restore annotations for now - -## 4.0.0-beta.161 - -## 4.0.0-beta.160 - -### Patch Changes - -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -## 4.0.0-beta.159 - -### Patch Changes - -- c1e73de: - -## 4.0.0-beta.158 - -### Patch Changes - -- 3c1f52d: improve: class strictness enabled by default again, allow `strict: false` as opt out for now. -- 6ae3050: Preserve class annotation parseOptions in relaxed declaration struct decoding so custom parse options (including concurrency defaults) are applied consistently. - -## 4.0.0-beta.157 - -### Patch Changes - -- 6fff09c: unify encoded function for when you use encodedKeys - -## 4.0.0-beta.156 - -## 4.0.0-beta.155 - -### Patch Changes - -- c215db8: align TaggedUnion with array arg - -## 4.0.0-beta.154 - -## 4.0.0-beta.153 - -## 4.0.0-beta.152 - -## 4.0.0-beta.151 - -## 4.0.0-beta.150 - -### Patch Changes - -- 85a8275: Expose `make`, `makeOption`, and `makeEffect` static helpers on request classes created via `Query`/`Command`. - -## 4.0.0-beta.149 - -### Patch Changes - -- f317c5e: Preserve omitted-input make helpers through Schema.Opaque when the wrapped schema allows optional or default-only constructor input. - -## 4.0.0-beta.148 - -### Patch Changes - -- 199e9a5: Allow Struct and TaggedStruct make helpers to omit input when every constructor field is optional or defaulted, and preserve widening copy typings through a lighter named public type to improve TypeScript editor responsiveness. - -## 4.0.0-beta.147 - -### Patch Changes - -- 47e3742: Preserve Struct.copy through `annotate`, `annotateKey`, and `mapFields` chains, and add tests covering chained copy behavior on Struct schemas. - -## 4.0.0-beta.146 - -### Patch Changes - -- a4dff57: Adjust Struct and TaggedStruct copy typing to follow class-style widening constraints while keeping structural copy runtime behavior. - -## 4.0.0-beta.145 - -### Patch Changes - -- 12abb55: Refine schema copy behavior by keeping class copy constructor-based while using structural copy for Struct and TaggedStruct helpers. - -## 4.0.0-beta.144 - -### Patch Changes - -- 11422f8: Update request helper typing and runtime invocation to rely on schema `.make` instead of class constructors, avoiding `new`-based assumptions for request schemas. -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. - -## 4.0.0-beta.143 - -### Patch Changes - -- 79eb019: Remove redundant schema `title` annotations when they duplicate the schema `identifier`. - -## 4.0.0-beta.142 - -### Minor Changes - -- 025de47: Fold the encoded-override support from `ExtendedClass` and `ExtendedTaggedClass` into `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass`, and update model codegen to detect the new second-generic form. - -### Patch Changes - -- 3436d44: Extend `Schema.Opaque` in `effect-app/Schema` to support an optional encoded-type generic while preserving the original single-generic behavior. - -## 4.0.0-beta.141 - -### Patch Changes - -- 7c25dbb: Add relaxed wrapper support for `ErrorClass` and `TaggedErrorClass` in `effect-app/Schema`, matching the existing class wrapper behavior (`copy`, cached `ast`, and unbounded parse concurrency). - -## 4.0.0-beta.140 - -## 4.0.0-beta.139 - -## 4.0.0-beta.138 - -## 4.0.0-beta.137 - -## 4.0.0-beta.136 - -## 4.0.0-beta.135 - -### Patch Changes - -- c7bbc41: fix - class should check type side - -## 4.0.0-beta.134 - -### Patch Changes - -- 9d3495e: Preserve field-level schema decode errors for relaxed Class and TaggedClass declarations so decode failures report nested constraints (for example min-length violations) instead of only a generic class-type mismatch. -- f353d48: Rename the Class/TaggedClass relaxed declaration option to `strict` (default `false`) and apply it to `Class`, `TaggedClass`, `ExtendedClass`, and `ExtendedTaggedClass`. - - When `strict: true`, class decoding keeps strict class-level declaration behavior; by default, decoding remains relaxed and preserves field-level schema errors. - -## 4.0.0-beta.133 - -### Patch Changes - -- c3299f7: update packages - -## 4.0.0-beta.132 - -## 4.0.0-beta.131 - -## 4.0.0-beta.130 - -### Patch Changes - -- ea1bd46: fix: prevent expansion of nominal brands - -## 4.0.0-beta.129 - -## 4.0.0-beta.128 - -### Patch Changes - -- 57db551: Split `TaggedRequestFor` into `Query` and `Command` factories, and mark generated request classes with `type: "query" | "command"`. - - Vue client helpers now expose query-only helpers (`query`, `suspense`, `fetch`) for query requests and mutation-only helpers (`mutate`, `fetch`) for command requests. - -## 4.0.0-beta.127 - -## 4.0.0-beta.126 - -### Patch Changes - -- 458bb1b: fix type - -## 4.0.0-beta.125 - -## 4.0.0-beta.124 - -### Patch Changes - -- 256ae85: cleanup - -## 4.0.0-beta.123 - -### Patch Changes - -- 14aba14: fix: clientFor - -## 4.0.0-beta.122 - -### Patch Changes - -- f052d38: Replace `(...) => Effect.gen` with `Effect.fnUntraced` in `apiClientFactory`. - -## 4.0.0-beta.121 - -### Patch Changes - -- 5ac46cb: Make `Schema.provide` dual (pipeable) - -## 4.0.0-beta.120 - -### Minor Changes - -- f21190c: Add `copy` property to `Class` and `TaggedClass` for creating modified instances with updated fields. The `copy` method is cached per-class and supports both object and function-based updates, with pipeline support. - -## 4.0.0-beta.119 - -## 4.0.0-beta.118 - -### Minor Changes - -- 08d2e70: Add `concurrency: "unbounded"` parseOptions annotation to Schema constructors (Struct, Array, NonEmptyArray, Record, TaggedStruct, ReadonlySet, ReadonlyMap, Class, TaggedClass) so all encode/decode operations automatically run with unbounded concurrency. Also override `mapFields` on Struct and Class/TaggedClass to preserve the annotation. - -### Patch Changes - -- bd26832: Add nested anyOf flattening to JSON Schema post-processing -- Update effect packages to 4.0.0-beta.52 - -## 4.0.0-beta.117 - -## 4.0.0-beta.116 - -## 4.0.0-beta.115 - -## 4.0.0-beta.114 - -## 4.0.0-beta.113 - -## 4.0.0-beta.112 - -## 4.0.0-beta.111 - -### Patch Changes - -- ca94edf: fix typo - -## 4.0.0-beta.110 - -## 4.0.0-beta.109 - -## 4.0.0-beta.108 - -### Patch Changes - -- 3e46e7b: allow excess props in openapi schemas - -## 4.0.0-beta.107 - -## 4.0.0-beta.106 - -## 4.0.0-beta.105 - -## 4.0.0-beta.104 - -### Minor Changes - -- e944bca: Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - - - `generateGuards("key")` — generic per-guard, no need to specify the container type - - `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - - Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -## 4.0.0-beta.103 - -### Minor Changes - -- 7119320: Add `generateGuards` to `TaggedUnion` / `ExtendTaggedUnion` for generating property-scoped type guards. - - Usage: `MyUnion.generateGuards()("propertyKey")` returns `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -## 4.0.0-beta.102 - -## 4.0.0-beta.101 - -## 4.0.0-beta.100 - -## 4.0.0-beta.99 - -## 4.0.0-beta.98 - -## 4.0.0-beta.97 - -## 4.0.0-beta.96 - -### Patch Changes - -- 5615e47: bs - -## 4.0.0-beta.95 - -### Patch Changes - -- 88838fb: Remove pick/omit customizations from Class/TaggedClass/Struct/TaggedStruct. Use `Struct.pick(X.fields, [...])` from `effect-app` instead. - -## 4.0.0-beta.94 - -## 4.0.0-beta.93 - -## 4.0.0-beta.92 - -## 4.0.0-beta.91 - -### Minor Changes - -- 738b482: Add pick/omit to Struct and TaggedStruct - -## 4.0.0-beta.90 - -## 4.0.0-beta.89 - -## 4.0.0-beta.88 - -## 4.0.0-beta.87 - -## 4.0.0-beta.86 - -## 4.0.0-beta.85 - -## 4.0.0-beta.84 - -## 4.0.0-beta.83 - -## 4.0.0-beta.82 - -## 4.0.0-beta.81 - -## 4.0.0-beta.80 - -## 4.0.0-beta.79 - -### Minor Changes - -- d16845e: Remove `TaggedRequest` from `makeRpcClient`, now only `TaggedRequestFor` is returned. Remove all legacy `meta.moduleName` support — `id` and `moduleName` are now required on `Req` type. Remove `makeRpcGroup` (use `makeRpcGroupFromRequestsAndModuleName` instead). - -## 4.0.0-beta.78 - -## 4.0.0-beta.77 - -### Minor Changes - -- 3613e87: Add `TaggedRequestFor` helper to `makeRpcClient` that curries a `moduleName`, producing request classes with static `id` and `moduleName` properties. This enables passing request classes directly to `makeQueryKey` without going through `clientFor` first. The `clientFor` function no longer requires a `meta` property on the module when requests carry `moduleName`. The meta codegen preset now generates `Req = TaggedRequestFor(moduleName)`. Original `TaggedRequest` remains for backwards compatibility. - -## 4.0.0-beta.76 - -### Patch Changes - -- a5248a9: use new schema features -- fac725d: update effect to latest beta - -## 4.0.0-beta.75 - -### Patch Changes - -- 24f0a5a: rename Literal to Literals, no longer hiding Schema's singular Literal. - -## 4.0.0-beta.74 - -### Patch Changes - -- 54ec1ef: fix bs - -## 4.0.0-beta.73 - -## 4.0.0-beta.72 - -### Patch Changes - -- 0541f0d: fix withDefault types - -## 4.0.0-beta.71 - -### Minor Changes - -- beae3a0: Remove `withDefaultConstructor` wrapper, use `S.withConstructorDefault` directly with `Effect.succeed`/`Effect.sync`. - -## 4.0.0-beta.70 - -## 4.0.0-beta.69 - -### Patch Changes - -- dc465e3: update to latest effect beta - -## 4.0.0-beta.68 - -### Patch Changes - -- e6f2341: Add SecretURL tests and switch secretURL config to use Config.nonEmptyString - -## 4.0.0-beta.67 - -## 4.0.0-beta.66 - -### Patch Changes - -- edc52e4: update - -## 4.0.0-beta.65 - -### Minor Changes - -- 1f103b2: Replace `proxify` with explicit service accessor helpers: `accessFn`, `accessEffectFn`, `accessCn`, `accessEffectCn`. - -### Patch Changes - -- 31739d7: Fix JSON schema output for Email, Date, PhoneNumber, and Url schemas. The `jsonSchema` annotation key is not recognized by Effect v4's JSON schema generator — use proper v4 annotations (`format`, `description`) and built-in checks (`isMinLength`, `isMaxLength`) instead. - -## 4.0.0-beta.64 - -### Patch Changes - -- c1a6fdc: fix proxify - -## 4.0.0-beta.63 - -## 4.0.0-beta.62 - -### Patch Changes - -- 0b21a02: fix: flatten simple allOf constraints in JSON Schema output for better codegen compatibility - -## 4.0.0-beta.61 - -## 4.0.0-beta.60 - -## 4.0.0-beta.59 - -### Patch Changes - -- cec026d: update packages - -## 4.0.0-beta.58 - -## 4.0.0-beta.57 - -## 4.0.0-beta.56 - -## 4.0.0-beta.55 - -## 4.0.0-beta.54 - -### Patch Changes - -- d867272: the return of `Context` - -## 4.0.0-beta.53 - -### Patch Changes - -- ee9694e: fix type issue? - -## 4.0.0-beta.52 - -### Minor Changes - -- 6252808: Add `ReadonlySetFromArray` and `ReadonlyMapFromArray` schema transformations that decode from annotated arrays to Set/Map. Update `ReadonlySet` and `ReadonlyMap` to use these internally. - -## 4.0.0-beta.51 - -## 4.0.0-beta.50 - -## 4.0.0-beta.49 - -### Patch Changes - -- e585c9c: fix config nested - -## 4.0.0-beta.48 - -### Patch Changes - -- 0c88f78: Remove `TaggedError` compatibility re-export, use `TaggedErrorClass` directly - -## 4.0.0-beta.47 - -### Patch Changes - -- 3365758: workaround effect error messages - -## 4.0.0-beta.46 - -## 4.0.0-beta.45 - -### Patch Changes - -- 10b55ff: update packages - -## 4.0.0-beta.44 - -### Patch Changes - -- a37aa38: Update to effect beta 43 - -## 4.0.0-beta.43 - -## 4.0.0-beta.42 - -## 4.0.0-beta.41 - -## 4.0.0-beta.40 - -## 4.0.0-beta.39 - -### Patch Changes - -- 10e90d5: fix: TaggedRequest should be an Opaque Schema Class - -## 4.0.0-beta.38 - -### Patch Changes - -- 0b3e00e: fix bug - -## 4.0.0-beta.37 - -### Patch Changes - -- 947fe20: fix input date - -## 4.0.0-beta.36 - -## 4.0.0-beta.35 - -## 4.0.0-beta.34 - -### Patch Changes - -- 8c645d5: update to latest effect - -## 4.0.0-beta.33 - -### Patch Changes - -- 4b95009: use Finite instead of Number - -## 4.0.0-beta.32 - -### Patch Changes - -- 01d862a: again - -## 4.0.0-beta.31 - -### Patch Changes - -- 6b6d601: fix ForceVOid - -## 4.0.0-beta.30 - -### Patch Changes - -- 5ec1f45: fix ForceVoid for real - -## 4.0.0-beta.29 - -### Patch Changes - -- a899d46: Replace `ForceVoid` in `makeClient` with a typed codec transformation instead of an unsafe cast. - -## 4.0.0-beta.28 - -### Patch Changes - -- 0099208: Refactor `Pure` helpers to use typed `useSync` calls instead of unsafe casts. - -## 4.0.0-beta.27 - -### Patch Changes - -- 601a1ff: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it - -## 4.0.0-beta.26 - -### Patch Changes - -- 4da28e2: Change `Schema.Class` and `Schema.TaggedClass` wrappers to default constructor options to `{ disableValidation: true }`. - - This avoids strict class identifier validation by default when constructing wrapper classes (for example passing a compatible view class), while keeping existing behavior when explicit options are provided. - -## 4.0.0-beta.25 - -### Patch Changes - -- 6282d4b: Update `Effect.allLower` to call `svc.asEffect()` when available, ensuring service entries are normalized before `Effect.all` evaluation. - -## 4.0.0-beta.24 - -### Patch Changes - -- 32f71bf: Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs. - - Add tests covering: - - - `TaggedUnion` with `encodeKeys`-wrapped members - - `TaggedUnion` with `TaggedClass` members - -## 4.0.0-beta.23 - -### Patch Changes - -- ea08a79: remove `fields` constraint from TaggedUnion/tags helpers, extract `_tag` via AST walk instead - -## 4.0.0-beta.22 - -## 4.0.0-beta.21 - -### Patch Changes - -- 0d349e7: Align effect-app tagged union helpers with Effect v4 by delegating to the native tagged union utilities, exposing v4-style `cases` and `guards`, and preserving the local `tags` helper. -- 75c72ee: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined - -## 4.0.0-beta.20 - -### Patch Changes - -- aa921db: Fix `TaggedRequest` no-config error type inference so requests without a third argument infer the same default error schema as requests with explicit success config. - -## 4.0.0-beta.19 - -### Patch Changes - -- ffc10a4: Migrate `Schema/ext` context helpers to Effect v4 by implementing `provide` and `contextFromServices` with proper service subtraction across decoding and encoding requirements. - -## 4.0.0-beta.18 - -### Patch Changes - -- e9dbbdd: Tighten schema helper generics in `Schema/ext.ts` so default-related helpers accept properly named schema type parameters instead of bare `S.Top` inputs. - -## 4.0.0-beta.17 - -### Patch Changes - -- c8a6a3f: fix TaggedUnion - -## 4.0.0-beta.16 - -### Patch Changes - -- 56db2c4: fix: isA - -## 4.0.0-beta.15 - -### Patch Changes - -- 20186b6: Migrate schema arbitrary annotations to Effect v4 `toArbitrary` and cover `StringId` generation with a fast-check test. - -## 4.0.0-beta.14 - -## 4.0.0-beta.13 - -### Patch Changes - -- 16de88c: remove hopefully obsolete Struct overrides -- 1c3c541: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- de2359d: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- 0d72f34: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. - -## 4.0.0-beta.12 - -### Patch Changes - -- 622d54a: Configure Changesets fixed versioning for public packages. - -## 4.0.0-beta.10 - -### Patch Changes - -- 01c70d0: update all teh tings - -## 4.0.0-beta.9 - -### Patch Changes - -- 5727372: switch to NdJson - -## 4.0.0-beta.8 - -### Patch Changes - -- 1f336bc: fix RequestName - -## 4.0.0-beta.7 - -### Patch Changes - -- 62b4989: fix request attr - -## 4.0.0-beta.6 - -### Patch Changes - -- df75041: fix Req - -## 4.0.0-beta.5 - -### Patch Changes - -- 016c5a3: adapt isObject change - -## 4.0.0-beta.4 - -### Patch Changes - -- 88b90c3: fix withDefault - -## 4.0.0-beta.3 - -### Patch Changes - -- 3a7abae: fix bs - -## 4.0.0-beta.2 - -### Major Changes - -- 3887256: Fix Schema->Codec - -## 4.0.0-beta.1 - -### Patch Changes - -- 64786af: Beta25 - -## 4.0.0-beta.0 - -### Major Changes - -- 880df28: Effect v4 beta - -## 3.16.0 - -### Minor Changes - -- 1a102e2: update packages - -## 3.15.1 - -### Patch Changes - -- 049b54a: improve: add NEA support to Array.groupByT. - -## 3.15.0 - -### Minor Changes - -- ecbd35f: drop default proxy - -## 3.14.2 - -### Patch Changes - -- faf7e9d: add utils, fix bugs - -## 3.14.1 - -### Patch Changes - -- c5a2bd5: add dropUndefinedRecursive - -## 3.14.0 - -### Minor Changes - -- 9316017: feat: add `dropUndefinedT2` - -### Patch Changes - -- 358782e: update packages - -## 3.13.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -## 3.12.0 - -### Minor Changes - -- 23ba90f: improve: compare deep structurally - -## 3.11.0 - -### Minor Changes - -- 9697a93: feat: Map withDefault - -## 3.10.2 - -### Patch Changes - -- fdf1781: enhance runtimeFiberAsPromise with abort. - -## 3.10.1 - -### Patch Changes - -- fe49192: improve literal default, overridable - -## 3.10.0 - -### Minor Changes - -- 51ded8d: add "withDefault" to Schema.Literal. - -## 3.9.0 - -### Minor Changes - -- b8f5e30: update packages - -## 3.8.1 - -### Patch Changes - -- 493fbb8: fix tagged class pick/omit omitting \_tag. - -## 3.8.1-next.0 - -### Patch Changes - -- fix tagged class pick/omit omitting \_tag. - -## 3.8.0 - -### Minor Changes - -- 3cb4c4a: simplify requests/client, drop raw for now, fix missing schema context in request handlers - -## 3.7.1 - -### Patch Changes - -- 4da7f79: cleanup helpers namespace - -## 3.7.0 - -### Minor Changes - -- 6d8c512: update packages - -## 3.6.1 - -### Patch Changes - -- d38a50a: align S.Class to original, adding missing make. - -## 3.6.0 - -### Minor Changes - -- 3d49d81: update packages - -## 3.5.0 - -### Minor Changes - -- 68e91c9: rename `name` to `id` - -## 3.4.0 - -### Minor Changes - -- 923f0d8: improve defaultFailureMessageHandler - -## 3.3.7 - -### Patch Changes - -- 4000c95: track const action name - -## 3.3.6 - -### Patch Changes - -- 8096576: new eslint shared config package - -## 3.3.5 - -### Patch Changes - -- 47e83ca: update cli - -## 3.3.4 - -### Patch Changes - -- f9aee77: some fixes - -## 3.3.3 - -### Patch Changes - -- f6a3328: export base eslint files so that we import them in projects - -## 3.3.2 - -### Patch Changes - -- f5da9d0: update packages - -## 3.3.1 - -### Patch Changes - -- d670d5a: runtimefiber errors should be wrapped - -## 3.3.0 - -### Minor Changes - -- d50dfe6: add support for RuntimeFiber, fix should return the Promise for eventOnSubmit. - -## 3.2.4 - -### Patch Changes - -- 8cb893c: dprint rules the world - -## 3.2.3 - -### Patch Changes - -- f101ef0: upgrade ea - -## 3.2.2 - -### Patch Changes - -- 33f100d: finalize new cli - -## 3.2.1 - -### Patch Changes - -- c12a49b: effect app cli to effect cli - -## 3.2.0 - -### Minor Changes - -- bd12e89: Disable Schema batching by default, not to run into unexpected endless waiting for resolving to finish. - -## 3.1.0 - -### Minor Changes - -- 1e4de39: remove namespace/type merging in Prelude. - -### Patch Changes - -- 41352d8: refactor: remove internal usage of namespace/type collission. - -## 3.0.10 - -### Patch Changes - -- ce91007: fix non inverted rpc context map use - -## 3.0.9 - -### Patch Changes - -- ea6380f: cleanup RpcContextMap - -## 3.0.8 - -### Patch Changes - -- 3543bfb: cleanup dev-mode - -## 3.0.7 - -### Patch Changes - -- d42afe6: cleanup - -## 3.0.6 - -### Patch Changes - -- 868eab6: cleanup - -## 3.0.5 - -### Patch Changes - -- 182ca78: improve: MiddlewareMaker is a Tag with Self so we can extend it from class. - -## 3.0.4 - -### Patch Changes - -- bc68d9f: feat: introduce Layer.make as alternative to Context.ProvideService - -## 3.0.3 - -### Patch Changes - -- c31423a: cleanup rpc - -## 3.0.2 - -### Patch Changes - -- 88bb989: align dynamic/global middleware with V4 - -## 3.0.1 - -### Patch Changes - -- 74c28d1: move common rpc out of infra - -## 3.0.0 - -### Major Changes - -- 3e4c45d: feat: Add new @effect/rpc Dynamic and Global Middleware, based on effect-app Router - -## 2.56.0 - -### Minor Changes - -- db2b575: switch to new HttpLayerRouter - -## 2.55.0 - -### Minor Changes - -- 37eb29a: improve ServiceBus api - -## 2.54.3 - -### Patch Changes - -- dd30471: improve - -## 2.54.2 - -### Patch Changes - -- 79ffd88: updates - -## 2.54.1 - -### Patch Changes - -- 9f90266: fix prefix namespace - -## 2.54.0 - -### Minor Changes - -- 0f2b2cf: update packages - -## 2.53.1 - -### Patch Changes - -- fa2c8d0: fix prefix - -## 2.53.0 - -### Minor Changes - -- 5c51597: refactor Middleware to run as RPC middleware - -## 2.52.1 - -### Patch Changes - -- 244f84d: various improvements, documentation - -## 2.52.0 - -### Minor Changes - -- 99cf15f: unify middleware api - -## 2.51.0 - -### Minor Changes - -- 54dd59a: cleanup NES - -## 2.50.2 - -### Patch Changes - -- 0d33085: fix dts freak - -## 2.50.1 - -### Patch Changes - -- build fix - -## 2.50.0 - -### Minor Changes - -- 0a82e7f: update packages - -## 2.49.0 - -### Minor Changes - -- a36ceed: feat: composable dynamic middleware - -## 2.48.0 - -### Minor Changes - -- 8bb246b: cleanup and document router/middleware - -## 2.47.2 - -### Patch Changes - -- fup - -## 2.47.1 - -### Patch Changes - -- fup - -## 2.47.0 - -### Minor Changes - -- d29cb1b: refactor: drop optional identifier for Schema classes. - -## 2.46.1 - -### Patch Changes - -- b095bb5: tscp - -## 2.46.0 - -### Minor Changes - -- ba4ad65: update packages; drop ServiceStrict in favour of language-service - -## 2.45.0 - -### Minor Changes - -- 2c217ce: update packages - -## 2.44.0 - -### Minor Changes - -- 55da9e7: improve: Router supports contextProvider through middleware, replacing flawed requestLayers - -## 2.43.3 - -### Patch Changes - -- b771967: update packages - -## 2.43.2 - -### Patch Changes - -- aec6960: fix complex ast tagged union - -## 2.43.1 - -### Patch Changes - -- a2e1e1e: improve jsonschema of Email - -## 2.43.0 - -### Minor Changes - -- eac9c67: update packages - -## 2.42.1 - -### Patch Changes - -- 49d93a5: update packages - -## 2.42.0 - -### Minor Changes - -- ba96171: improve Cosmos errors - -## 2.41.0 - -### Minor Changes - -- b1d4af4: housekeeping: update packages - -## 2.40.1 - -### Patch Changes - -- 0230385: improve: rpc spans - - fewer duplicate spans - - improve name - -## 2.40.0 - -### Minor Changes - -- 8cb33d5: update packages - -## 2.39.1 - -### Patch Changes - -- d3b05cb: update packages -- 4627c7b: improve: optimise getFirstById. -- 1e1f32b: housekeeping: update packages - -## 2.39.0 - -### Minor Changes - -- bdc9d5c: feat: add support for skippable namespaces in query keys - -## 2.38.5 - -### Patch Changes - -- 8b7fdbe: update packages - -## 2.38.4 - -### Patch Changes - -- revert copyOrigin changes - -## 2.38.3 - -### Patch Changes - -- update packages - -## 2.38.2 - -### Patch Changes - -- c0ed28a: preserve readonlyness in the returned type for copyOrigin -- 0fcab38: update packages - -## 2.38.1 - -### Patch Changes - -- cd15a8e: some fixes for copyOrigin - -## 2.38.0 - -### Minor Changes - -- 81479bf: add specialized copy function wrt a constructor - -## 2.37.1 - -### Patch Changes - -- 7bedb13: updates - -## 2.37.0 - -### Minor Changes - -- 14ea804: housekeeping: update packages - -## 2.36.1 - -### Patch Changes - -- 4c8dfb4: update packages - -## 2.36.0 - -### Minor Changes - -- e42d5fb: update to effect 3.14 with native rpc2 - -## 2.35.0 - -### Minor Changes - -- f38e31f: make LogLevels readonly - -### Patch Changes - -- f30b57e: fix faker and rebuild dts - -## 2.34.0 - -### Minor Changes - -- 64d3a54: refactor: replace custom FiberRefs with Context.Reference in preparation of Effect v4 - -### Patch Changes - -- 771ac45: update packages - -## 2.33.1 - -### Patch Changes - -- 36996ed: housekeeping: update packages - -## 2.33.0 - -### Minor Changes - -- 1e88a86: improve error handled marking - -## 2.32.3 - -### Patch Changes - -- 63e966a: update packages - -## 2.32.2 - -### Patch Changes - -- 96158a1: remove manual error headers/body gathering. - -## 2.32.1 - -### Patch Changes - -- a42d5a6: mark also number for brand - -## 2.32.0 - -### Minor Changes - -- bca03c7: feat: enhance Types module - -## 2.31.1 - -### Patch Changes - -- 24417c7: lol - -## 2.31.0 - -### Minor Changes - -- 9ebae95: improve: rpc error reporting - -## 2.30.0 - -### Minor Changes - -- 10a8adf: improve: lock rpc responses to 418/422/200, report otherwise as response error! helping with bad proxies in between. -- 8be2984: housekeeping: update packages - -## 2.29.0 - -### Minor Changes - -- 7a4d393: report errors anyway to Sentry but with lower severity. add log namespace annotations. - -## 2.28.0 - -### Minor Changes - -- 32730a4: improve error reporting - -## 2.27.0 - -### Minor Changes - -- 9b2e385: update packages - -## 2.26.3 - -### Patch Changes - -- 02c4141: fix type - -## 2.26.2 - -### Patch Changes - -- 411c3d5: fix type - -## 2.26.1 - -### Patch Changes - -- 709c9cc: fix missing \_tag on TaggedClass - -## 2.26.0 - -### Minor Changes - -- 2d0ad81: update packages - -## 2.25.0 - -### Minor Changes - -- f597755: feat: remove void check and force void conversion at runtime. - -## 2.24.3 - -### Patch Changes - -- 9d11dfb: housekeeping: update packages - -## 2.24.2 - -### Patch Changes - -- 42e6776: fix Map error - -## 2.24.1 - -### Patch Changes - -- 469f28d: fix: groupByT should support any key type -- ab9d1ea: housekeeping: update packages - -## 2.24.0 - -### Minor Changes - -- 3fa0eda: fix: stream error, change to nostream - -## 2.23.2 - -### Patch Changes - -- 1c3af52: fix HttpClient type - -## 2.23.1 - -### Patch Changes - -- 93e88c2: fix: export - -## 2.23.0 - -### Minor Changes - -- c8ebfb4: update packages - -## 2.22.0 - -### Minor Changes - -- 67023f8: kill \_cjs - -## 2.21.0 - -### Minor Changes - -- b1396c9: refactor: rename ApiClient to ApiClientFactory - -## 2.20.0 - -### Minor Changes - -- 3388736: feat: add `cause` support to errors - -### Patch Changes - -- 9f504c6: update packages - -## 2.19.5 - -### Patch Changes - -- 47eb94e: cleanup legacy annotations - -## 2.19.4 - -### Patch Changes - -- fix: return type - -## 2.19.3 - -### Patch Changes - -- 750b46a: update packages - -## 2.19.2 - -### Patch Changes - -- 1bfc3d8: update packages - -## 2.19.1 - -### Patch Changes - -- 5ef6481: update packages - -## 2.19.0 - -### Minor Changes - -- 29a1a65: update packages - -## 2.18.0 - -### Minor Changes - -- fe7783b: improve: TaggedUnion .is is now S.is, isA, is the old is - -## 2.17.0 - -### Minor Changes - -- 47a543b: remove transform-paths as default plugin -- d84aef7: update packages - -## 2.16.0 - -### Minor Changes - -- cc2c43e: update packages - -## 2.15.0 - -### Minor Changes - -- improve: clientFor as part of service so that cache is also fine. - -## 2.14.0 - -### Minor Changes - -- a30da33: improve: cleanup client legacy -- 9301abb: cleanup legacy - -## 2.13.3 - -### Patch Changes - -- 125e2ea: update packages - -## 2.13.2 - -### Patch Changes - -- 54f700f: fix: missing empty config - -## 2.13.1 - -### Patch Changes - -- 0feb32c: update packages - -## 2.13.0 - -### Minor Changes - -- bump - -## 2.12.0 - -### Minor Changes - -- 82363f6: add: Schema.contextFromServices - -## 2.11.2 - -### Patch Changes - -- c7c76b4: update packages - -## 2.11.1 - -### Patch Changes - -- c971d73: update packages - -## 2.11.0 - -### Minor Changes - -- update - -## 2.10.0 - -### Minor Changes - -- f19762b: update packages - -## 2.9.0 - -### Minor Changes - -- 565d678: improve: strict dependencies for routing and services. - -## 2.8.2 - -### Patch Changes - -- c2fa0ed: fix error schema - -## 2.8.1 - -### Patch Changes - -- 1e83e5c: fix type - -## 2.8.0 - -### Minor Changes - -- ba1c7fc: feat: add support for specifying success fields directly. - closes #102 - -### Patch Changes - -- 412cb3d: update packages - -## 2.7.0 - -### Minor Changes - -- 590e5bf: update packages - -## 2.6.0 - -### Minor Changes - -- d4ab73f: update packages - -## 2.5.2 - -### Patch Changes - -- 19ea035: update packages - -## 2.5.1 - -### Patch Changes - -- 724a1a8: error bs - -## 2.5.0 - -### Minor Changes - -- e95cab7: improve: also treat cosmos 409 error as OptimisticConcurrencyException - -## 2.4.0 - -### Minor Changes - -- d5623a6: update packages - -## 2.3.0 - -### Minor Changes - -- 9ef18f6: adapt Prelude workaround - -### Patch Changes - -- 9ef18f6: adapt Prelude workaround - -## 2.2.1 - -### Patch Changes - -- f68dbd2: update packages - -## 2.2.0 - -### Minor Changes - -- 02c3f55: refactor: restructure infra - -## 2.1.1 - -### Patch Changes - -- bs - -## 2.1.0 - -### Minor Changes - -- a34ba2a: add Schema.provide - -## 2.0.2 - -### Patch Changes - -- 25b154d: fix circular - -## 2.0.1 - -### Patch Changes - -- 39e4f3d: fix: missing export - -## 2.0.0 - -### Major Changes - -- 13abe12: refactor: merge core into prelude - -## 1.32.0 - -### Minor Changes - -- d0471b1: refactor: merge @effect-app/schema into core - -### Patch Changes - -- Updated dependencies [d0471b1] - - @effect-app/core@1.19.0 - -## 1.31.0 - -### Minor Changes - -- 3872061: migrate to effect/Schema - -### Patch Changes - -- Updated dependencies [3872061] - - @effect-app/schema@1.20.0 - - @effect-app/core@1.18.0 - -## 1.30.5 - -### Patch Changes - -- e836dcc: cleanup - -## 1.30.4 - -### Patch Changes - -- 0eac9da: improve: Operation error handling - -## 1.30.3 - -### Patch Changes - -- 4a814f5: improve error report - -## 1.30.2 - -### Patch Changes - -- cda1477: update packages -- Updated dependencies [cda1477] - - @effect-app/schema@1.19.1 - - @effect-app/core@1.17.1 - -## 1.30.1 - -### Patch Changes - -- cleanup - -## 1.30.0 - -### Minor Changes - -- c069b8e: add WithOptionalSpan - -## 1.29.0 - -### Minor Changes - -- 3d4e45a: cleanup FiberSets - -## 1.28.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.19.0 - - @effect-app/core@1.17.0 - -## 1.27.0 - -### Minor Changes - -- 9ddb31b: update packages - -### Patch Changes - -- Updated dependencies [9ddb31b] - - @effect-app/schema@1.18.0 - - @effect-app/core@1.16.0 - -## 1.26.8 - -### Patch Changes - -- 0ea3023: drop pipe ext, should've known -- Updated dependencies [0ea3023] - - @effect-app/core@1.15.8 - - @effect-app/schema@1.17.8 - -## 1.26.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.7 - - @effect-app/schema@1.17.7 - -## 1.26.6 - -### Patch Changes - -- Updated dependencies [c57fd1b] - - @effect-app/core@1.15.6 - - @effect-app/schema@1.17.6 - -## 1.26.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.5 - - @effect-app/schema@1.17.5 - -## 1.26.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.4 - - @effect-app/schema@1.17.4 - -## 1.26.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.3 - - @effect-app/schema@1.17.3 - -## 1.26.2 - -### Patch Changes - -- Updated dependencies [56b764d] - - @effect-app/core@1.15.2 - - @effect-app/schema@1.17.2 - -## 1.26.1 - -### Patch Changes - -- Updated dependencies [56df0f3] - - @effect-app/core@1.15.1 - - @effect-app/schema@1.17.1 - -## 1.26.0 - -### Minor Changes - -- 0effd97: update packages - -### Patch Changes - -- Updated dependencies [0effd97] - - @effect-app/schema@1.17.0 - - @effect-app/core@1.15.0 - -## 1.25.0 - -### Minor Changes - -- a2ac359: add pipe extensions experiment - -### Patch Changes - -- Updated dependencies [a2ac359] - - @effect-app/core@1.14.0 - - @effect-app/schema@1.16.2 - -## 1.24.1 - -### Patch Changes - -- Updated dependencies [61eb517] - - @effect-app/core@1.13.1 - - @effect-app/schema@1.16.1 - -## 1.24.0 - -### Minor Changes - -- b9b64b8: housekeeping: update packages - -### Patch Changes - -- Updated dependencies [b9b64b8] - - @effect-app/schema@1.16.0 - - @effect-app/core@1.13.0 - -## 1.23.1 - -### Patch Changes - -- 16bc3b2: fix typo - -## 1.23.0 - -### Minor Changes - -- 67a202a: fix location - -## 1.22.1 - -### Patch Changes - -- fup - -## 1.22.0 - -### Minor Changes - -- c2487d4: remove legacy FetchResponse - -## 1.21.1 - -### Patch Changes - -- 66086f3: f - -## 1.21.0 - -### Minor Changes - -- cleanup all legacy router cruft - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.15.0 - -## 1.20.0 - -### Minor Changes - -- 65dcc73: update packages - -### Patch Changes - -- Updated dependencies [65dcc73] - - @effect-app/schema@1.14.0 - - @effect-app/core@1.12.0 - -## 1.19.1 - -### Patch Changes - -- supply layer - -## 1.19.0 - -### Minor Changes - -- 4d2b804: housekeeping: update packages - -### Patch Changes - -- Updated dependencies [4d2b804] - - @effect-app/schema@1.13.0 - - @effect-app/core@1.11.0 - -## 1.18.0 - -### Minor Changes - -- b670052: feat: followup rpc client side - -## 1.17.5 - -### Patch Changes - -- 447cba4: fix paths - -## 1.17.4 - -### Patch Changes - -- 550ba43: control log levels -- Updated dependencies [550ba43] - - @effect-app/core@1.10.2 - - @effect-app/schema@1.12.2 - -## 1.17.3 - -### Patch Changes - -- 6a3e66a: feat: expose tags on TaggedUnion - -## 1.17.2 - -### Patch Changes - -- 13b7aa1: fix: TaggedUnion should inherit the rest of Union - -## 1.17.1 - -### Patch Changes - -- 38c0e65: cleanup - -## 1.17.0 - -### Minor Changes - -- 190b139: update packages - -## 1.16.1 - -### Patch Changes - -- c710953: update packagesy -- Updated dependencies [3de8bfb] - - @effect-app/core@1.10.1 - - @effect-app/schema@1.12.1 - -## 1.16.0 - -### Minor Changes - -- 4bc6e99: update packages -- 1018e58: update packages - -### Patch Changes - -- Updated dependencies [4bc6e99] -- Updated dependencies [1018e58] - - @effect-app/schema@1.12.0 - - @effect-app/core@1.10.0 - -## 1.15.5 - -### Patch Changes - -- d9fd446: fix type - -## 1.15.4 - -### Patch Changes - -- 70c6222: improve doc - -## 1.15.3 - -### Patch Changes - -- 97a0bf8: fix client type - -## 1.15.2 - -### Patch Changes - -- fb62f02: bwc - -## 1.15.1 - -### Patch Changes - -- 2b56d7e: cleanup routing -- 73d5e0a: cleanup routing - -## 1.15.0 - -### Minor Changes - -- feat: add new router support - -## 1.14.0 - -### Minor Changes - -- b3d9a3e: update packages - -### Patch Changes - -- Updated dependencies [b3d9a3e] - - @effect-app/schema@1.11.0 - - @effect-app/core@1.9.0 - -## 1.13.4 - -### Patch Changes - -- d08286c: update packages -- Updated dependencies [d08286c] - - @effect-app/schema@1.10.1 - - @effect-app/core@1.8.1 - -## 1.13.3 - -### Patch Changes - -- 6c81e3c: improve: remove framework stacktrace location - -## 1.13.2 - -### Patch Changes - -- a64695e: fix runtime error - -## 1.13.1 - -### Patch Changes - -- e5fc429: fix types - -## 1.13.0 - -### Minor Changes - -- c5f5223: update packages - -### Patch Changes - -- Updated dependencies [c5f5223] - - @effect-app/schema@1.10.0 - - @effect-app/core@1.8.0 - -## 1.12.5 - -### Patch Changes - -- Updated dependencies [5cb4b35] - - @effect-app/schema@1.9.5 - -## 1.12.4 - -### Patch Changes - -- Updated dependencies [0151436] - - @effect-app/schema@1.9.4 - -## 1.12.3 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/schema@1.9.3 - - @effect-app/core@1.7.5 - -## 1.12.2 - -### Patch Changes - -- e6eecd2: update packages -- Updated dependencies [e6eecd2] - - @effect-app/schema@1.9.2 - - @effect-app/core@1.7.4 - -## 1.12.1 - -### Patch Changes - -- ff -- Updated dependencies - - @effect-app/core@1.7.3 - - @effect-app/schema@1.9.1 - -## 1.12.0 - -### Minor Changes - -- 056f293: fix type - -### Patch Changes - -- Updated dependencies [056f293] - - @effect-app/schema@1.9.0 - -## 1.11.3 - -### Patch Changes - -- fix -- Updated dependencies - - @effect-app/core@1.7.2 - - @effect-app/schema@1.8.1 - -## 1.11.2 - -### Patch Changes - -- 65135b2: fix issue - -## 1.11.1 - -### Patch Changes - -- c229a73: add tagMap to TaggedUnion - -## 1.11.0 - -### Minor Changes - -- 24f0c0f: feat: allow custom errors per endpoint. - -### Patch Changes - -- Updated dependencies [24f0c0f] - - @effect-app/schema@1.8.0 - -## 1.10.2 - -### Patch Changes - -- 1ac74ec: cleanup tsconfig base -- 0b399ce: update packages -- Updated dependencies [0b399ce] - - @effect-app/schema@1.7.1 - - @effect-app/core@1.7.1 - -## 1.10.1 - -### Patch Changes - -- 9fbf129: fix: messagefallback - -## 1.10.0 - -### Minor Changes - -- 2143dd9: update packages - -### Patch Changes - -- Updated dependencies [2143dd9] - - @effect-app/schema@1.7.0 - - @effect-app/core@1.7.0 - -## 1.9.2 - -### Patch Changes - -- e120966: update packages -- f6066ac: update packages -- Updated dependencies [e120966] -- Updated dependencies [f6066ac] - - @effect-app/schema@1.6.1 - - @effect-app/core@1.6.1 - -## 1.9.1 - -### Patch Changes - -- 26551ce: improve rendering - -## 1.9.0 - -### Minor Changes - -- 79f58af: update packages - -### Patch Changes - -- Updated dependencies [79f58af] - - @effect-app/schema@1.6.0 - - @effect-app/core@1.6.0 - -## 1.8.2 - -### Patch Changes - -- b3ebc41: update packages -- Updated dependencies [b3ebc41] - - @effect-app/schema@1.5.2 - - @effect-app/core@1.5.2 - -## 1.8.1 - -### Patch Changes - -- f569681: update packages -- Updated dependencies [f569681] - - @effect-app/schema@1.5.1 - - @effect-app/core@1.5.1 - -## 1.8.0 - -### Minor Changes - -- d12fc1a: improve: expose interrupt, not waitUntilEmpty on FiberSets - -## 1.7.0 - -### Minor Changes - -- 613c148: update packages - -### Patch Changes - -- Updated dependencies [613c148] - - @effect-app/schema@1.5.0 - - @effect-app/core@1.5.0 - -## 1.6.0 - -### Minor Changes - -- 8d0c511: update packages - -### Patch Changes - -- Updated dependencies [8d0c511] - - @effect-app/schema@1.4.0 - - @effect-app/core@1.4.0 - -## 1.5.2 - -### Patch Changes - -- Updated dependencies [b48c630] - - @effect-app/schema@1.3.1 - -## 1.5.1 - -### Patch Changes - -- 8438eed: fix ConfigSecretURL - -## 1.5.0 - -### Minor Changes - -- 9cc6f86: update packages - -### Patch Changes - -- Updated dependencies [9cc6f86] - - @effect-app/schema@1.3.0 - - @effect-app/core@1.3.0 - -## 1.4.8 - -### Patch Changes - -- fe2754a: improve: decode span -- 349c421: Update packages -- Updated dependencies [349c421] - - @effect-app/schema@1.2.8 - - @effect-app/core@1.2.3 - -## 1.4.7 - -### Patch Changes - -- 744cf0f: add client.response span - -## 1.4.6 - -### Patch Changes - -- 9d944a1: update packages -- Updated dependencies [9d944a1] - - @effect-app/schema@1.2.7 - - @effect-app/core@1.2.2 - -## 1.4.5 - -### Patch Changes - -- a67d115: improve client trace - -## 1.4.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.6 - -## 1.4.3 - -### Patch Changes - -- 24c3fe9: align brands -- Updated dependencies [24c3fe9] - - @effect-app/schema@1.2.5 - -## 1.4.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.4 - -## 1.4.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.3 - -## 1.4.0 - -### Minor Changes - -- c10818c: improve forking - -### Patch Changes - -- 2c661c1: update packages -- Updated dependencies [2c661c1] - - @effect-app/schema@1.2.2 - - @effect-app/core@1.2.1 - -## 1.3.1 - -### Patch Changes - -- Updated dependencies [92f303f] - - @effect-app/schema@1.2.1 - -## 1.3.0 - -### Minor Changes - -- 9ac9416: update packages - -### Patch Changes - -- Updated dependencies [9ac9416] - - @effect-app/schema@1.2.0 - - @effect-app/core@1.2.0 - -## 1.2.5 - -### Patch Changes - -- revert - -## 1.2.4 - -### Patch Changes - -- 6335139: allow error - -## 1.2.3 - -### Patch Changes - -- 3411e78: update packages -- Updated dependencies [3411e78] - - @effect-app/schema@1.1.3 - - @effect-app/core@1.1.3 - -## 1.2.2 - -### Patch Changes - -- 128f636: update packages -- Updated dependencies [128f636] - - @effect-app/schema@1.1.2 - - @effect-app/core@1.1.2 - -## 1.2.1 - -### Patch Changes - -- Updated dependencies [132bbc4] - - @effect-app/core@1.1.1 - - @effect-app/schema@1.1.1 - -## 1.2.0 - -### Minor Changes - -- 2e29520: update packages - -### Patch Changes - -- Updated dependencies [2e29520] - - @effect-app/schema@1.1.0 - - @effect-app/core@1.1.0 - -## 1.1.1 - -### Patch Changes - -- 4dba4a5: add annotateSpanWithError - -## 1.1.0 - -### Minor Changes - -- b8b729e: adopt @effect-rx - -## 1.0.12 - -### Patch Changes - -- c99cdbb: update packages -- Updated dependencies [c99cdbb] - - @effect-app/schema@1.0.10 - - @effect-app/core@1.0.6 - -## 1.0.11 - -### Patch Changes - -- feat: improve error reported state. - -## 1.0.10 - -### Patch Changes - -- Updated dependencies [f202812] - - @effect-app/schema@1.0.9 - -## 1.0.9 - -### Patch Changes - -- 83f19e1: schema next minor -- Updated dependencies [83f19e1] - - @effect-app/schema@1.0.8 - -## 1.0.8 - -### Patch Changes - -- 86d33e8: add error reporter for vue - -## 1.0.7 - -### Patch Changes - -- c4dbb6c: update packages -- Updated dependencies [c4dbb6c] - - @effect-app/schema@1.0.7 - - @effect-app/core@1.0.5 - -## 1.0.6 - -### Patch Changes - -- 93f53d4: update packages -- Updated dependencies [93f53d4] - - @effect-app/schema@1.0.6 - - @effect-app/core@1.0.4 - -## 1.0.5 - -### Patch Changes - -- Updated dependencies [9c69b5b] - - @effect-app/core@1.0.3 - - @effect-app/schema@1.0.5 - -## 1.0.4 - -### Patch Changes - -- Updated dependencies [6363ff5] - - @effect-app/core@1.0.2 - - @effect-app/schema@1.0.4 - -## 1.0.3 - -### Patch Changes - -- bf702ba: update packages -- Updated dependencies [bf702ba] - - @effect-app/schema@1.0.3 - - @effect-app/core@1.0.1 - -## 1.0.2 - -### Patch Changes - -- Updated dependencies [156664e] - - @effect-app/schema@1.0.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.0.1 - -## 1.0.0 - -### Major Changes - -- 4e9992f: effect 3.0! - -### Patch Changes - -- Updated dependencies [4e9992f] - - @effect-app/schema@1.0.0 - - @effect-app/core@1.0.0 - -## 0.204.0 - -### Minor Changes - -- version prob - -## 0.203.1 - -### Patch Changes - -- ff5d466: cleanup Struct type -- Updated dependencies [ff5d466] - - @effect-app/schema@0.221.1 - -## 0.203.0 - -### Minor Changes - -- 0d1b378: update to latest schema - -### Patch Changes - -- Updated dependencies [0d1b378] - - @effect-app/schema@0.221.0 - - @effect-app/core@0.170.0 - -## 0.202.1 - -### Patch Changes - -- 45770ea: re-add MainFiberSet join logging - -## 0.202.0 - -### Minor Changes - -- c0a2bb1: rename FiberBag to MainFiberSet - -## 0.201.6 - -### Patch Changes - -- 4a242e9: use built in FiberSet.join - -## 0.201.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.169.1 - - @effect-app/schema@0.220.4 - -## 0.201.4 - -### Patch Changes - -- 36fb254: fix emptyto check - -## 0.201.3 - -### Patch Changes - -- Updated dependencies [0ad9ea9] - - @effect-app/schema@0.220.3 - -## 0.201.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.220.2 - -## 0.201.1 - -### Patch Changes - -- Updated dependencies [89bdcf0] - - @effect-app/schema@0.220.1 - -## 0.201.0 - -### Minor Changes - -- 290a709: update packages - -### Patch Changes - -- Updated dependencies [290a709] - - @effect-app/schema@0.220.0 - - @effect-app/core@0.169.0 - -## 0.200.0 - -### Minor Changes - -- 564a95c: fix branded schemas - -### Patch Changes - -- Updated dependencies [564a95c] - - @effect-app/schema@0.219.0 - -## 0.199.0 - -### Minor Changes - -- ecb05f6: update packages -- fbfa538: add title to Operations and expose all - -### Patch Changes - -- Updated dependencies [ecb05f6] - - @effect-app/schema@0.218.0 - - @effect-app/core@0.168.0 - -## 0.198.2 - -### Patch Changes - -- Updated dependencies [7b1ff1c] - - @effect-app/core@0.167.0 - - @effect-app/schema@0.217.2 - -## 0.198.1 - -### Patch Changes - -- Updated dependencies [e165c07] - - @effect-app/schema@0.217.1 - -## 0.198.0 - -### Minor Changes - -- 01f8283: cleanup: drop legacy `FromClass` - -### Patch Changes - -- Updated dependencies [01f8283] - - @effect-app/schema@0.217.0 - -## 0.197.0 - -### Minor Changes - -- revert form changes - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.166.0 - - @effect-app/schema@0.216.0 - -## 0.196.2 - -### Patch Changes - -- Updated dependencies [2044733] - - @effect-app/core@0.165.0 - - @effect-app/schema@0.215.2 - -## 0.196.1 - -### Patch Changes - -- Updated dependencies [a9a81a1] - - @effect-app/core@0.164.0 - - @effect-app/schema@0.215.1 - -## 0.196.0 - -### Minor Changes - -- 7a3f05c: update packages - -### Patch Changes - -- Updated dependencies [7a3f05c] - - @effect-app/core@0.163.0 - - @effect-app/schema@0.215.0 - -## 0.195.0 - -### Minor Changes - -- bbaa558: bs - -### Patch Changes - -- Updated dependencies [bbaa558] - - @effect-app/core@0.162.0 - - @effect-app/schema@0.214.0 - -## 0.194.1 - -### Patch Changes - -- Updated dependencies [ac37ff7] - - @effect-app/fluent-extensions@0.155.1 - - @effect-app/core@0.161.1 - - @effect-app/schema@0.213.1 - -## 0.194.0 - -### Minor Changes - -- e5b0d8e: update packages -- 2e2fa98: update packages -- 44ef913: update packages - -### Patch Changes - -- Updated dependencies [e5b0d8e] -- Updated dependencies [2e2fa98] -- Updated dependencies [44ef913] - - @effect-app/fluent-extensions@0.155.0 - - @effect-app/schema@0.213.0 - - @effect-app/core@0.161.0 - -## 0.193.0 - -### Minor Changes - -- 494842c: update packages - -### Patch Changes - -- Updated dependencies [494842c] - - @effect-app/fluent-extensions@0.154.0 - - @effect-app/schema@0.212.0 - - @effect-app/core@0.160.0 - -## 0.192.1 - -### Patch Changes - -- Updated dependencies [413bdee] - - @effect-app/core@0.159.1 - - @effect-app/schema@0.211.2 - -## 0.192.0 - -### Minor Changes - -- 215071f: cleanup TagClass -- af56c32: Integrate Service in Context - -### Patch Changes - -- Updated dependencies [af56c32] - - @effect-app/core@0.159.0 - - @effect-app/schema@0.211.1 - -## 0.191.0 - -### Minor Changes - -- cb84068: replace lodash with builtin and cleanup - -### Patch Changes - -- Updated dependencies [cb84068] - - @effect-app/fluent-extensions@0.153.0 - - @effect-app/schema@0.211.0 - - @effect-app/core@0.158.0 - -## 0.190.0 - -### Minor Changes - -- 790bfc2: cleanup - -### Patch Changes - -- Updated dependencies [790bfc2] - - @effect-app/fluent-extensions@0.152.0 - - @effect-app/core@0.157.0 - - @effect-app/schema@0.210.2 - -## 0.189.1 - -### Patch Changes - -- 0334285: update packages -- Updated dependencies [0334285] -- Updated dependencies [b9f35eb] - - @effect-app/fluent-extensions@0.151.0 - - @effect-app/schema@0.210.1 - - @effect-app/core@0.156.1 - -## 0.189.0 - -### Minor Changes - -- 498deb0: update packages - -### Patch Changes - -- Updated dependencies [498deb0] - - @effect-app/fluent-extensions@0.150.0 - - @effect-app/schema@0.210.0 - - @effect-app/core@0.156.0 - -## 0.188.0 - -### Minor Changes - -- fd8f019: update packages - -### Patch Changes - -- Updated dependencies [fd8f019] - - @effect-app/fluent-extensions@0.149.0 - - @effect-app/schema@0.209.0 - - @effect-app/core@0.155.0 - -## 0.187.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.208.1 - -## 0.187.0 - -### Minor Changes - -- 51253a2: update packages - -### Patch Changes - -- Updated dependencies [51253a2] - - @effect-app/fluent-extensions@0.148.0 - - @effect-app/schema@0.208.0 - - @effect-app/core@0.154.0 - -## 0.186.2 - -### Patch Changes - -- 0a0b8e9: add ServiceUnavailableError - -## 0.186.1 - -### Patch Changes - -- Updated dependencies [df2a290] - - @effect-app/schema@0.207.1 - -## 0.186.0 - -### Minor Changes - -- f2c3553: update packages - -### Patch Changes - -- Updated dependencies [f2c3553] - - @effect-app/fluent-extensions@0.147.0 - - @effect-app/schema@0.207.0 - - @effect-app/core@0.153.0 - -## 0.185.8 - -### Patch Changes - -- Updated dependencies [2b53680] - - @effect-app/schema@0.206.7 - -## 0.185.7 - -### Patch Changes - -- Updated dependencies [2794b0a] - - @effect-app/schema@0.206.6 - -## 0.185.6 - -### Patch Changes - -- Updated dependencies [41bec85] - - @effect-app/schema@0.206.5 - -## 0.185.5 - -### Patch Changes - -- Updated dependencies [511d81b] - - @effect-app/schema@0.206.4 - -## 0.185.4 - -### Patch Changes - -- df3a550: fup - -## 0.185.3 - -### Patch Changes - -- f385129: fix legacy struct use -- Updated dependencies [f385129] - - @effect-app/schema@0.206.3 - -## 0.185.2 - -### Patch Changes - -- Updated dependencies [fe910fe] - - @effect-app/schema@0.206.2 - -## 0.185.1 - -### Patch Changes - -- Updated dependencies [dbc7c83] - - @effect-app/schema@0.206.1 - -## 0.185.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.146.0 - - @effect-app/schema@0.206.0 - - @effect-app/core@0.152.1 - -## 0.184.0 - -### Minor Changes - -- 5178025: update packages - -### Patch Changes - -- Updated dependencies [5178025] - - @effect-app/fluent-extensions@0.145.0 - - @effect-app/schema@0.205.0 - - @effect-app/core@0.152.0 - -## 0.183.2 - -### Patch Changes - -- c7b8007: bag it - -## 0.183.1 - -### Patch Changes - -- Updated dependencies [58233dd] - - @effect-app/core@0.151.1 - - @effect-app/schema@0.204.1 - -## 0.183.0 - -### Minor Changes - -- 77366cc: don't go through service constructor - -## 0.182.1 - -### Patch Changes - -- f188543: update accessor shape - -## 0.182.0 - -### Minor Changes - -- d0a1774: update ts to 5.4.1-rc - -### Patch Changes - -- Updated dependencies [d0a1774] - - @effect-app/fluent-extensions@0.144.0 - - @effect-app/schema@0.204.0 - - @effect-app/core@0.151.0 - -## 0.181.8 - -### Patch Changes - -- Updated dependencies [ce64bc5] - - @effect-app/schema@0.203.4 - -## 0.181.7 - -### Patch Changes - -- Updated dependencies [bbf24a6] - - @effect-app/schema@0.203.3 - -## 0.181.6 - -### Patch Changes - -- Updated dependencies [e12b4f8] - - @effect-app/schema@0.203.2 - -## 0.181.5 - -### Patch Changes - -- Updated dependencies [09bc0d5] - - @effect-app/schema@0.203.1 - -## 0.181.4 - -### Patch Changes - -- Updated dependencies [e355a45] - - @effect-app/schema@0.203.0 - -## 0.181.3 - -### Patch Changes - -- Updated dependencies [f4bea89] - - @effect-app/fluent-extensions@0.143.4 - - @effect-app/core@0.150.5 - - @effect-app/schema@0.202.5 - -## 0.181.2 - -### Patch Changes - -- Updated dependencies [ea3dd75] - - @effect-app/fluent-extensions@0.143.3 - - @effect-app/core@0.150.4 - - @effect-app/schema@0.202.4 - -## 0.181.1 - -### Patch Changes - -- 579f42d: fix typo - -## 0.181.0 - -### Minor Changes - -- 752958a: awesome tag - -## 0.180.10 - -### Patch Changes - -- abb5332: add ServiceShape - -## 0.180.9 - -### Patch Changes - -- Updated dependencies [cef7a30] - - @effect-app/fluent-extensions@0.143.2 - - @effect-app/core@0.150.3 - - @effect-app/schema@0.202.3 - -## 0.180.8 - -### Patch Changes - -- 190415c: fix - -## 0.180.7 - -### Patch Changes - -- 6ece717: base FiberBag on FiberSet - -## 0.180.6 - -### Patch Changes - -- dba2711: fix generic - -## 0.180.5 - -### Patch Changes - -- e470bd5: add forkDaemon and forkScoped in FiberBag. - -## 0.180.4 - -### Patch Changes - -- Updated dependencies [81f152f] - - @effect-app/core@0.150.2 - - @effect-app/schema@0.202.2 - -## 0.180.3 - -### Patch Changes - -- add fiberbag - -## 0.180.2 - -### Patch Changes - -- Updated dependencies [ef4b0ba] - - @effect-app/fluent-extensions@0.143.1 - - @effect-app/core@0.150.1 - - @effect-app/schema@0.202.1 - -## 0.180.1 - -### Patch Changes - -- f2eea61: fix TagClass - -## 0.180.0 - -### Minor Changes - -- 7346691: update packages - -### Patch Changes - -- Updated dependencies [7346691] - - @effect-app/fluent-extensions@0.143.0 - - @effect-app/schema@0.202.0 - - @effect-app/core@0.150.0 - -## 0.179.0 - -### Minor Changes - -- add AcessTag support - -## 0.178.1 - -### Patch Changes - -- fix - -## 0.178.0 - -### Minor Changes - -- 0354650: prototype - -## 0.177.0 - -### Minor Changes - -- 32f852e: add requestname to span - -## 0.176.2 - -### Patch Changes - -- Updated dependencies [d1e4f1b] - - @effect-app/schema@0.201.2 - -## 0.176.1 - -### Patch Changes - -- Updated dependencies [0b514ad] - - @effect-app/schema@0.201.1 - -## 0.176.0 - -### Minor Changes - -- e8e8ee2: update packages - -### Patch Changes - -- Updated dependencies [e8e8ee2] - - @effect-app/fluent-extensions@0.142.0 - - @effect-app/schema@0.201.0 - - @effect-app/core@0.149.0 - -## 0.175.1 - -### Patch Changes - -- Updated dependencies [ee00776] - - @effect-app/schema@0.200.0 - -## 0.175.0 - -### Minor Changes - -- 117be64: bump - -### Patch Changes - -- Updated dependencies [117be64] - - @effect-app/core@0.148.0 - - @effect-app/fluent-extensions@0.141.0 - - @effect-app/schema@0.199.0 - -## 0.174.1 - -### Patch Changes - -- 4008eb1: cleanup and add byIdAndSaveWithPure - -## 0.174.0 - -### Minor Changes - -- A new day -- 78b3c9e: reduce tspuse - -### Patch Changes - -- Updated dependencies -- Updated dependencies [78b3c9e] - - @effect-app/core@0.147.0 - - @effect-app/fluent-extensions@0.140.0 - - @effect-app/schema@0.198.0 - -## 0.173.4 - -### Patch Changes - -- 9577608: cleanup - -## 0.173.3 - -### Patch Changes - -- 4d9b13c: add extendTaggedUnion - -## 0.173.2 - -### Patch Changes - -- Updated dependencies [3170396] - - @effect-app/core@0.146.1 - - @effect-app/schema@0.197.1 - -## 0.173.1 - -### Patch Changes - -- ac8e9aa: add OperationResult.is - -## 0.173.0 - -### Minor Changes - -- c070e0d: update effect - -### Patch Changes - -- dd0cf42: update -- Updated dependencies [c070e0d] -- Updated dependencies [dd0cf42] - - @effect-app/fluent-extensions@0.139.0 - - @effect-app/schema@0.197.0 - - @effect-app/core@0.146.0 - -## 0.172.0 - -### Minor Changes - -- 57fcfef: update packages - -### Patch Changes - -- 739e742: add missing plugins in tsconfig -- Updated dependencies [57fcfef] - - @effect-app/fluent-extensions@0.138.0 - - @effect-app/schema@0.196.0 - - @effect-app/core@0.145.0 - -## 0.171.4 - -### Patch Changes - -- Updated dependencies [b5543d4] - - @effect-app/core@0.144.3 - - @effect-app/schema@0.195.3 - -## 0.171.3 - -### Patch Changes - -- Updated dependencies [39beb09] - - @effect-app/core@0.144.2 - - @effect-app/schema@0.195.2 - -## 0.171.2 - -### Patch Changes - -- 0fd4175: f - -## 0.171.1 - -### Patch Changes - -- 73c3e1b: update packages -- Updated dependencies [73c3e1b] - - @effect-app/fluent-extensions@0.137.1 - - @effect-app/core@0.144.1 - - @effect-app/schema@0.195.1 - -## 0.171.0 - -### Minor Changes - -- 49fb62c: align Tag arg order. - -## 0.170.0 - -### Minor Changes - -- 76ea9ae: improve - -## 0.169.0 - -### Minor Changes - -- 7079fc8: connection reset by peer - -### Patch Changes - -- Updated dependencies [7079fc8] - - @effect-app/fluent-extensions@0.137.0 - - @effect-app/schema@0.195.0 - - @effect-app/core@0.144.0 - -## 0.168.0 - -### Minor Changes - -- update TagClassId - -## 0.167.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.136.1 - - @effect-app/core@0.143.1 - - @effect-app/schema@0.194.2 - -## 0.167.1 - -### Patch Changes - -- Updated dependencies [c4ac357] - - @effect-app/core@0.143.0 - - @effect-app/schema@0.194.1 - -## 0.167.0 - -### Minor Changes - -- 8374f61: update packages - -### Patch Changes - -- Updated dependencies [8374f61] - - @effect-app/fluent-extensions@0.136.0 - - @effect-app/schema@0.194.0 - - @effect-app/core@0.142.0 - -## 0.166.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.141.1 - - @effect-app/schema@0.193.1 - -## 0.166.0 - -### Minor Changes - -- de336f6: src - -### Patch Changes - -- Updated dependencies [de336f6] - - @effect-app/fluent-extensions@0.135.0 - - @effect-app/schema@0.193.0 - - @effect-app/core@0.141.0 - -## 0.165.2 - -### Patch Changes - -- dbbd0da: fix path - -## 0.165.1 - -### Patch Changes - -- 7715b74: add tsconfig.base.json -- f10e630: update packages -- Updated dependencies [f10e630] - - @effect-app/fluent-extensions@0.134.1 - - @effect-app/schema@0.192.1 - - @effect-app/core@0.140.1 - -## 0.165.0 - -### Minor Changes - -- 47f46f0: use actual action name casing so that editor go to works. - -## 0.164.0 - -### Minor Changes - -- 5f52e06: update packages - -### Patch Changes - -- Updated dependencies [5f52e06] - - @effect-app/fluent-extensions@0.134.0 - - @effect-app/schema@0.192.0 - - @effect-app/core@0.140.0 - -## 0.163.0 - -### Minor Changes - -- m - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.139.0 - - @effect-app/fluent-extensions@0.133.0 - - @effect-app/schema@0.191.0 - -## 0.162.3 - -### Patch Changes - -- 5b30ea1: cleanup - -## 0.162.2 - -### Patch Changes - -- fix name - -## 0.162.1 - -### Patch Changes - -- fix name - -## 0.162.0 - -### Minor Changes - -- bs - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.138.0 - - @effect-app/fluent-extensions@0.132.0 - - @effect-app/schema@0.190.0 - -## 0.161.0 - -### Minor Changes - -- 8794c23: bs - -## 0.160.0 - -### Minor Changes - -- 797056c: update packages - -### Patch Changes - -- Updated dependencies [797056c] - - @effect-app/fluent-extensions@0.131.0 - - @effect-app/core@0.137.0 - - @effect-app/schema@0.189.1 - -## 0.159.0 - -### Minor Changes - -- 6145788: switch to tanstack-query - -## 0.158.5 - -### Patch Changes - -- b4ee7e7: f - -## 0.158.4 - -### Patch Changes - -- pf - -## 0.158.3 - -### Patch Changes - -- Updated dependencies [c31e783] - - @effect-app/fluent-extensions@0.130.3 - -## 0.158.2 - -### Patch Changes - -- 4848bc6: bump -- Updated dependencies [4848bc6] - - @effect-app/fluent-extensions@0.130.2 - -## 0.158.1 - -### Patch Changes - -- Updated dependencies [67571c9] - - @effect-app/fluent-extensions@0.130.1 - -## 0.158.0 - -### Minor Changes - -- 12eb7f7: update packages - -### Patch Changes - -- Updated dependencies [12eb7f7] - - @effect-app/fluent-extensions@0.130.0 - - @effect-app/schema@0.189.0 - - @effect-app/core@0.136.0 - -## 0.157.4 - -### Patch Changes - -- Updated dependencies [b525b4f] - - @effect-app/fluent-extensions@0.129.3 - -## 0.157.3 - -### Patch Changes - -- Updated dependencies [aed6cd9] - - @effect-app/fluent-extensions@0.129.2 - -## 0.157.2 - -### Patch Changes - -- Updated dependencies [37c167f] - - @effect-app/fluent-extensions@0.129.1 - -## 0.157.1 - -### Patch Changes - -- 28feba6: fix var - -## 0.157.0 - -### Minor Changes - -- 17cfe0f: update packages - -### Patch Changes - -- Updated dependencies [17cfe0f] - - @effect-app/fluent-extensions@0.129.0 - - @effect-app/schema@0.188.0 - - @effect-app/core@0.135.0 - -## 0.156.5 - -### Patch Changes - -- Updated dependencies [3c1cb25] - - @effect-app/core@0.134.1 - - @effect-app/fluent-extensions@0.128.5 - - @effect-app/schema@0.187.5 - -## 0.156.4 - -### Patch Changes - -- Updated dependencies [eddbf3c] - - @effect-app/core@0.134.0 - - @effect-app/fluent-extensions@0.128.4 - - @effect-app/schema@0.187.4 - -## 0.156.3 - -### Patch Changes - -- 7c28686: update packages -- Updated dependencies [7c28686] - - @effect-app/fluent-extensions@0.128.3 - - @effect-app/schema@0.187.3 - - @effect-app/core@0.133.3 - -## 0.156.2 - -### Patch Changes - -- Updated dependencies [963b8d0] - - @effect-app/core@0.133.2 - - @effect-app/fluent-extensions@0.128.2 - - @effect-app/schema@0.187.2 - -## 0.156.1 - -### Patch Changes - -- b9e1e73: ext -- Updated dependencies [b9e1e73] - - @effect-app/core@0.133.1 - - @effect-app/fluent-extensions@0.128.1 - - @effect-app/schema@0.187.1 - -## 0.156.0 - -### Minor Changes - -- 9b67f67: update packages - -### Patch Changes - -- Updated dependencies [9b67f67] - - @effect-app/fluent-extensions@0.128.0 - - @effect-app/schema@0.187.0 - - @effect-app/core@0.133.0 - -## 0.155.2 - -### Patch Changes - -- b3e095c: fix - -## 0.155.1 - -### Patch Changes - -- fix - -## 0.155.0 - -### Minor Changes - -- 71dbfc3: update packages -- 1cc7ee7: cleanup service tags - -### Patch Changes - -- Updated dependencies [71dbfc3] - - @effect-app/fluent-extensions@0.127.0 - - @effect-app/schema@0.186.0 - - @effect-app/core@0.132.0 - -## 0.154.3 - -### Patch Changes - -- Updated dependencies [8577c90] - - @effect-app/core@0.131.9 - - @effect-app/fluent-extensions@0.126.10 - - @effect-app/schema@0.185.2 - -## 0.154.2 - -### Patch Changes - -- 7db0be1: dual copy/clone -- Updated dependencies [7db0be1] - - @effect-app/core@0.131.8 - - @effect-app/fluent-extensions@0.126.9 - - @effect-app/schema@0.185.1 - -## 0.154.1 - -### Patch Changes - -- Updated dependencies [ac55751] - - @effect-app/schema@0.185.0 - - @effect-app/fluent-extensions@0.126.8 - -## 0.154.0 - -### Minor Changes - -- 83091eb: cleanup - -## 0.153.7 - -### Patch Changes - -- Updated dependencies [dc45062] - - @effect-app/core@0.131.7 - - @effect-app/fluent-extensions@0.126.7 - - @effect-app/schema@0.184.7 - -## 0.153.6 - -### Patch Changes - -- Updated dependencies [fc86a75] - - @effect-app/core@0.131.6 - - @effect-app/fluent-extensions@0.126.6 - - @effect-app/schema@0.184.6 - -## 0.153.5 - -### Patch Changes - -- Updated dependencies [e47df05] - - @effect-app/core@0.131.5 - - @effect-app/fluent-extensions@0.126.5 - - @effect-app/schema@0.184.5 - -## 0.153.4 - -### Patch Changes - -- Updated dependencies [3db43d9] - - @effect-app/core@0.131.4 - - @effect-app/fluent-extensions@0.126.4 - - @effect-app/schema@0.184.4 - -## 0.153.3 - -### Patch Changes - -- Updated dependencies [8dd650f] - - @effect-app/core@0.131.3 - - @effect-app/fluent-extensions@0.126.3 - - @effect-app/schema@0.184.3 - -## 0.153.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.131.2 - - @effect-app/fluent-extensions@0.126.2 - - @effect-app/schema@0.184.2 - -## 0.153.1 - -### Patch Changes - -- Updated dependencies [27e6867] - - @effect-app/core@0.131.1 - - @effect-app/fluent-extensions@0.126.1 - - @effect-app/schema@0.184.1 - -## 0.153.0 - -### Minor Changes - -- c8a07bc: fix path - -### Patch Changes - -- Updated dependencies [c8a07bc] - - @effect-app/core@0.131.0 - - @effect-app/fluent-extensions@0.126.0 - - @effect-app/schema@0.184.0 - -## 0.152.0 - -### Minor Changes - -- 525237e: rename @effect-app/prelude to effect-app - -### Patch Changes - -- Updated dependencies [525237e] - - @effect-app/core@0.130.0 - - @effect-app/fluent-extensions@0.125.9 - - @effect-app/schema@0.183.14 - -## 0.151.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.129.3 - - @effect-app/fluent-extensions@0.125.8 - - @effect-app/schema@0.183.13 - -## 0.151.2 - -### Patch Changes - -- Updated dependencies [0d02cbd] - - @effect-app/core@0.129.2 - - @effect-app/fluent-extensions@0.125.7 - - @effect-app/schema@0.183.12 - -## 0.151.1 - -### Patch Changes - -- Updated dependencies [b01fd1e] - - @effect-app/core@0.129.1 - - @effect-app/fluent-extensions@0.125.6 - - @effect-app/schema@0.183.11 - -## 0.151.0 - -### Minor Changes - -- b2d3e53: consolidate - -### Patch Changes - -- Updated dependencies [b2d3e53] - - @effect-app/core@0.129.0 - - @effect-app/fluent-extensions@0.125.5 - - @effect-app/schema@0.183.10 - -## 0.150.3 - -### Patch Changes - -- 200a840: address -- Updated dependencies [200a840] - - @effect-app/core@0.128.2 - - @effect-app/fluent-extensions@0.125.4 - - @effect-app/schema@0.183.9 - -## 0.150.2 - -### Patch Changes - -- Updated dependencies [0c57301] - - @effect-app/core@0.128.1 - - @effect-app/fluent-extensions@0.125.3 - - @effect-app/schema@0.183.8 - -## 0.150.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.128.0 - - @effect-app/fluent-extensions@0.125.2 - - @effect-app/schema@0.183.7 - -## 0.150.0 - -### Minor Changes - -- 83b5bb6: fix option -- 87753e4: improve prelude export - -### Patch Changes - -- Updated dependencies [83b5bb6] -- Updated dependencies [87753e4] - - @effect-app/core@0.127.0 - - @effect-app/fluent-extensions@0.125.1 - - @effect-app/schema@0.183.6 - -## 0.149.6 - -### Patch Changes - -- 988ed1d: update packages -- Updated dependencies [38bff6b] -- Updated dependencies [988ed1d] - - @effect-app/fluent-extensions@0.125.0 - - @effect-app/core@0.126.1 - - @effect-app/schema@0.183.5 - -## 0.149.5 - -### Patch Changes - -- Updated dependencies [c497ff3] - - @effect-app/schema@0.183.4 - - @effect-app/fluent-extensions@0.124.4 - -## 0.149.4 - -### Patch Changes - -- 3c4251e: fix - -## 0.149.3 - -### Patch Changes - -- Updated dependencies [65f8b7b] - - @effect-app/schema@0.183.3 - - @effect-app/fluent-extensions@0.124.3 - -## 0.149.2 - -### Patch Changes - -- Updated dependencies [8290a87] - - @effect-app/schema@0.183.2 - - @effect-app/fluent-extensions@0.124.2 - -## 0.149.1 - -### Patch Changes - -- Updated dependencies [d17dc32] - - @effect-app/schema@0.183.1 - - @effect-app/fluent-extensions@0.124.1 - -## 0.149.0 - -### Minor Changes - -- e5cdd42: update packages - -### Patch Changes - -- Updated dependencies [e5cdd42] - - @effect-app/fluent-extensions@0.124.0 - - @effect-app/schema@0.183.0 - - @effect-app/core@0.126.0 - -## 0.148.0 - -### Minor Changes - -- update to latest Effect "minor" - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.123.0 - - @effect-app/schema@0.182.0 - - @effect-app/core@0.125.0 - -## 0.147.0 - -### Minor Changes - -- c29b6ff: bump - -### Patch Changes - -- Updated dependencies [c29b6ff] - - @effect-app/core@0.124.0 - - @effect-app/fluent-extensions@0.122.0 - - @effect-app/schema@0.181.0 - -## 0.145.3 - -### Patch Changes - -- 6e9f253: fix requestid withDefault - -## 0.146.0 - -### Minor Changes - -- 0c47b83: update packages - -### Patch Changes - -- Updated dependencies [0c47b83] - - @effect-app/fluent-extensions@0.121.0 - - @effect-app/schema@0.180.0 - - @effect-app/core@0.123.0 - -## 0.145.2 - -### Patch Changes - -- Updated dependencies [9d31709] - - @effect-app/fluent-extensions@0.120.0 - -## 0.145.1 - -### Patch Changes - -- 871cccf: remove cpy - -## 0.145.0 - -### Minor Changes - -- bump - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.119.0 - - @effect-app/schema@0.179.0 - - @effect-app/core@0.122.0 - -## 0.144.0 - -### Minor Changes - -- d740bf6: use schema as patch instead - -### Patch Changes - -- Updated dependencies [d740bf6] - - @effect-app/schema@0.178.0 - - @effect-app/fluent-extensions@0.118.1 - -## 0.143.11 - -### Patch Changes - -- Updated dependencies [028af99] - - @effect-app/fluent-extensions@0.118.0 - -## 0.143.10 - -### Patch Changes - -- Updated dependencies [8217bcd] - - @effect-app/fluent-extensions@0.117.4 - -## 0.143.9 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.117.3 - -## 0.143.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.116.6 - -## 0.143.7 - -### Patch Changes - -- Updated dependencies [e1c9d4c] - - @effect-app/fluent-extensions@0.117.0 - -## 0.143.6 - -### Patch Changes - -- Updated dependencies [cf3080b] - - @effect-app/fluent-extensions@0.116.6 - -## 0.143.5 - -### Patch Changes - -- Updated dependencies [e279f74] - - @effect-app/fluent-extensions@0.116.5 - -## 0.143.4 - -### Patch Changes - -- Updated dependencies [fb25eb5] - - @effect-app/fluent-extensions@0.116.4 - -## 0.143.3 - -### Patch Changes - -- Updated dependencies [34b8449] - - @effect-app/fluent-extensions@0.116.3 - -## 0.143.2 - -### Patch Changes - -- Updated dependencies [67d4ec9] - - @effect-app/fluent-extensions@0.116.2 - -## 0.143.1 - -### Patch Changes - -- Updated dependencies [bc98aeb] - - @effect-app/schema@0.177.5 - - @effect-app/fluent-extensions@0.116.1 - -## 0.143.0 - -### Minor Changes - -- e6c8452: remove global Either - -### Patch Changes - -- Updated dependencies [e6c8452] - - @effect-app/fluent-extensions@0.116.0 - - @effect-app/core@0.121.0 - - @effect-app/schema@0.177.4 - -## 0.142.3 - -### Patch Changes - -- Updated dependencies [acbbe35] - - @effect-app/schema@0.177.3 - - @effect-app/fluent-extensions@0.115.2 - -## 0.142.2 - -### Patch Changes - -- Updated dependencies [39cc9b0] - - @effect-app/fluent-extensions@0.115.1 - -## 0.142.1 - -### Patch Changes - -- 78de0f5: fix - -## 0.142.0 - -### Minor Changes - -- b51e7b3: ext - -### Patch Changes - -- Updated dependencies [b51e7b3] - - @effect-app/fluent-extensions@0.115.0 - -## 0.141.3 - -### Patch Changes - -- Updated dependencies [6806433] - - @effect-app/fluent-extensions@0.114.3 - -## 0.141.2 - -### Patch Changes - -- Updated dependencies [f28cec3] - - @effect-app/fluent-extensions@0.114.2 - -## 0.141.1 - -### Patch Changes - -- Updated dependencies [6de437e] - - @effect-app/fluent-extensions@0.114.1 - -## 0.141.0 - -### Minor Changes - -- 72e0db6: package updates - -### Patch Changes - -- Updated dependencies [72e0db6] - - @effect-app/fluent-extensions@0.114.0 - - @effect-app/core@0.120.0 - - @effect-app/schema@0.177.2 - -## 0.140.8 - -### Patch Changes - -- Updated dependencies [e66dbfc] - - @effect-app/fluent-extensions@0.113.13 - -## 0.140.7 - -### Patch Changes - -- Updated dependencies [6677889] - - @effect-app/fluent-extensions@0.113.12 - - @effect-app/schema@0.177.1 - -## 0.140.6 - -### Patch Changes - -- Updated dependencies [78982c7] - - @effect-app/fluent-extensions@0.113.11 - -## 0.140.5 - -### Patch Changes - -- Updated dependencies [928dfc8] - - @effect-app/fluent-extensions@0.113.10 - -## 0.140.4 - -### Patch Changes - -- Updated dependencies [2ab1f78] - - @effect-app/fluent-extensions@0.113.9 - -## 0.140.3 - -### Patch Changes - -- Updated dependencies [287c4e6] - - @effect-app/schema@0.177.0 - - @effect-app/fluent-extensions@0.113.8 - -## 0.140.2 - -### Patch Changes - -- Updated dependencies [bff7d58] - - @effect-app/schema@0.176.2 - - @effect-app/fluent-extensions@0.113.7 - -## 0.140.1 - -### Patch Changes - -- Updated dependencies [f7da591] - - @effect-app/schema@0.176.1 - - @effect-app/fluent-extensions@0.113.6 - -## 0.140.0 - -### Minor Changes - -- 76c89b8: cleanup exts - -### Patch Changes - -- Updated dependencies [76c89b8] -- Updated dependencies [76c89b8] - - @effect-app/schema@0.176.0 - - @effect-app/core@0.119.0 - - @effect-app/fluent-extensions@0.113.5 - -## 0.139.0 - -### Minor Changes - -- 2ccca0f: update exts - -### Patch Changes - -- Updated dependencies [ad46a20] -- Updated dependencies [40d51e4] -- Updated dependencies [2ccca0f] - - @effect-app/schema@0.175.0 - - @effect-app/fluent-extensions@0.113.4 - -## 0.138.0 - -### Minor Changes - -- b487764: improve extensions - -### Patch Changes - -- Updated dependencies [b487764] - - @effect-app/fluent-extensions@0.113.0 - -## 0.137.0 - -### Minor Changes - -- 465afb5: fix: Schema functions should use ParseError variants - -### Patch Changes - -- Updated dependencies [465afb5] - - @effect-app/core@0.118.0 - - @effect-app/schema@0.174.0 - -## 0.136.0 - -### Minor Changes - -- f01680a: update packages -- 349d358: improve support for schemas with R. - -### Patch Changes - -- ebcd0dc: update packages -- Updated dependencies [ebcd0dc] -- Updated dependencies [f01680a] -- Updated dependencies [349d358] - - @effect-app/schema@0.173.0 - -## 0.135.0 - -### Minor Changes - -- 744e5fc: disable auto constructor call - -### Patch Changes - -- 0a898f8: update packages -- Updated dependencies [0a898f8] - - @effect-app/schema@0.172.1 - -## 0.134.0 - -### Minor Changes - -- 449b343: update packages - -### Patch Changes - -- Updated dependencies [449b343] -- Updated dependencies [d103a32] - - @effect-app/schema@0.172.0 - -## 0.133.0 - -### Minor Changes - -- 2fd03d3: update packages - -### Patch Changes - -- Updated dependencies [2fd03d3] - - @effect-app/schema@0.171.0 - - @effect-app/core@0.117.0 - -## 0.132.1 - -### Patch Changes - -- c80a6bb: fix type - -## 0.132.0 - -### Minor Changes - -- 99f79ab: update packages -- cd25b5f: improve: typed error handling - -### Patch Changes - -- Updated dependencies [99f79ab] - - @effect-app/schema@0.170.0 - - @effect-app/core@0.116.0 - -## 0.131.0 - -### Minor Changes - -- 352d57c: cleanup query/mutations. - -## 0.130.5 - -### Patch Changes - -- Updated dependencies [17dbbff] - - @effect-app/schema@0.169.3 - -## 0.130.4 - -### Patch Changes - -- 578e25c: pick POST for query/delete when not all members assignable to string -- Updated dependencies [578e25c] - - @effect-app/schema@0.169.2 - -## 0.130.3 - -### Patch Changes - -- fix - -## 0.130.2 - -### Patch Changes - -- f5d1644: fix - -## 0.130.1 - -### Patch Changes - -- Updated dependencies [cb62aaa] - - @effect-app/schema@0.169.1 - -## 0.130.0 - -### Minor Changes - -- 1832f49: add auto method support - -### Patch Changes - -- Updated dependencies [1832f49] - - @effect-app/schema@0.169.0 - -## 0.129.0 - -### Minor Changes - -- default paths to / - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.168.0 - -## 0.128.2 - -### Patch Changes - -- 7db2745: meta - -## 0.128.1 - -### Patch Changes - -- f725d15: fix js - -## 0.128.0 - -### Minor Changes - -- 5786fb6: prefix path with request name - -## 0.127.2 - -### Patch Changes - -- Updated dependencies [690624e] - - @effect-app/schema@0.167.0 - -## 0.127.1 - -### Patch Changes - -- 9780ef0: fup - -## 0.127.0 - -### Minor Changes - -- a30db88: update packages -- b454cd7: embrace Matcher - -### Patch Changes - -- Updated dependencies [a30db88] -- Updated dependencies [b454cd7] - - @effect-app/schema@0.166.0 - - @effect-app/core@0.115.0 - -## 0.126.0 - -### Minor Changes - -- ed813b4: update packages - -### Patch Changes - -- Updated dependencies [ed813b4] - - @effect-app/schema@0.165.0 - -## 0.125.0 - -### Minor Changes - -- 3eb4057: update packages - -### Patch Changes - -- Updated dependencies [3eb4057] - - @effect-app/schema@0.164.0 - - @effect-app/core@0.114.0 - -## 0.124.0 - -### Minor Changes - -- 051bad7: override reasonable Email/PhoneNumber - -### Patch Changes - -- Updated dependencies [051bad7] - - @effect-app/core@0.113.0 - - @effect-app/schema@0.163.1 - -## 0.123.0 - -### Minor Changes - -- 4c915ea: update packages - -### Patch Changes - -- Updated dependencies [4c915ea] - - @effect-app/schema@0.163.0 - - @effect-app/core@0.112.0 - -## 0.122.1 - -### Patch Changes - -- Updated dependencies [e356144] - - @effect-app/schema@0.162.1 - -## 0.122.0 - -### Minor Changes - -- fix exts - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.111.0 - - @effect-app/schema@0.162.0 - -## 0.121.0 - -### Minor Changes - -- e2b5df8: revert workaround - -### Patch Changes - -- Updated dependencies [e2b5df8] - - @effect-app/schema@0.161.0 - - @effect-app/core@0.110.0 - -## 0.120.0 - -### Minor Changes - -- efa846c: improve: class workaround - -### Patch Changes - -- Updated dependencies [efa846c] - - @effect-app/schema@0.160.0 - - @effect-app/core@0.109.0 - -## 0.119.2 - -### Patch Changes - -- d3bc95b: fix lazy arb - -## 0.119.1 - -### Patch Changes - -- Updated dependencies [3936865] - - @effect-app/schema@0.159.1 - -## 0.119.0 - -### Minor Changes - -- 4aaa360: update packages -- a64728d: update to latest Schema - -### Patch Changes - -- Updated dependencies [4aaa360] -- Updated dependencies [a64728d] - - @effect-app/schema@0.159.0 - - @effect-app/core@0.108.0 - -## 0.118.0 - -### Minor Changes - -- 3ee1505: refactor: remove \_\_call extensions - -### Patch Changes - -- Updated dependencies [3ee1505] - - @effect-app/core@0.107.0 - - @effect-app/schema@0.158.1 - -## 0.117.0 - -### Minor Changes - -- 33dcf2e: update packages - -### Patch Changes - -- Updated dependencies [33dcf2e] - - @effect-app/schema@0.158.0 - - @effect-app/core@0.106.0 - -## 0.116.1 - -### Patch Changes - -- fd233aa: package updates -- Updated dependencies [5fe664e] -- Updated dependencies [afa98cf] - - @effect-app/core@0.105.0 - - @effect-app/schema@0.157.1 - -## 0.116.0 - -### Minor Changes - -- e535d05: update packages - -### Patch Changes - -- Updated dependencies [e535d05] - - @effect-app/schema@0.157.0 - - @effect-app/core@0.104.0 - -## 0.115.0 - -### Minor Changes - -- 9049ccd: filter path params from qs - -### Patch Changes - -- Updated dependencies [9049ccd] - - @effect-app/core@0.103.0 - - @effect-app/schema@0.156.1 - -## 0.114.0 - -### Minor Changes - -- a894a63: update packages - -### Patch Changes - -- Updated dependencies [a894a63] - - @effect-app/schema@0.156.0 - - @effect-app/core@0.102.0 - -## 0.113.0 - -### Minor Changes - -- 4e2e01b: roll into one - -## 0.112.1 - -### Patch Changes - -- 59077e8: feat: add `TagClassMakeScoped` - -## 0.112.0 - -### Minor Changes - -- 7945f38: update packages - -### Patch Changes - -- bc5f735: add `TagClassMake` -- Updated dependencies [7945f38] - - @effect-app/schema@0.155.0 - - @effect-app/core@0.101.0 - -## 0.111.0 - -### Minor Changes - -- 067274a: Update packages - -### Patch Changes - -- Updated dependencies [067274a] - - @effect-app/schema@0.154.0 - -## 0.110.0 - -### Minor Changes - -- 63425a7: update packages - -### Patch Changes - -- Updated dependencies [63425a7] - - @effect-app/schema@0.153.0 - - @effect-app/core@0.100.0 - -## 0.109.3 - -### Patch Changes - -- fix Positive->NonNegative - -## 0.109.2 - -### Patch Changes - -- 4464df4: missing ext - -## 0.109.1 - -### Patch Changes - -- Updated dependencies [fd85bed] - - @effect-app/schema@0.152.1 - -## 0.109.0 - -### Minor Changes - -- 82f0f0d: update packages - -### Patch Changes - -- Updated dependencies [82f0f0d] - - @effect-app/schema@0.152.0 - - @effect-app/core@0.99.0 - -## 0.108.3 - -### Patch Changes - -- Updated dependencies [153a19f] - - @effect-app/core@0.98.1 - - @effect-app/schema@0.151.2 - -## 0.108.2 - -### Patch Changes - -- c8e7d1d: improve: stack error helper on assign.tag - -## 0.108.1 - -### Patch Changes - -- 26e8e9f: improve: use meta info for request name -- 0f8297a: feat: add class annotate decorator -- Updated dependencies [0f8297a] - - @effect-app/schema@0.151.1 - -## 0.108.0 - -### Minor Changes - -- 2ea1e3c: update packages - -### Patch Changes - -- Updated dependencies [2ea1e3c] - - @effect-app/schema@0.151.0 - - @effect-app/core@0.98.0 - -## 0.107.11 - -### Patch Changes - -- Updated dependencies [cf69a10] - - @effect-app/schema@0.150.10 - -## 0.107.10 - -### Patch Changes - -- 5564b85: update packages -- Updated dependencies [5564b85] - - @effect-app/schema@0.150.9 - - @effect-app/core@0.97.3 - -## 0.107.9 - -### Patch Changes - -- f8b45b9: export numbers -- Updated dependencies [f8b45b9] - - @effect-app/schema@0.150.8 - -## 0.107.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.150.7 - -## 0.107.7 - -### Patch Changes - -- Updated dependencies [a72f796] - - @effect-app/schema@0.150.6 - -## 0.107.6 - -### Patch Changes - -- c615f35: update packages -- Updated dependencies [c615f35] - - @effect-app/core@0.97.2 - - @effect-app/schema@0.150.5 - -## 0.107.5 - -### Patch Changes - -- Updated dependencies [d9034fe] - - @effect-app/schema@0.150.4 - -## 0.107.4 - -### Patch Changes - -- 38f3367: fix unsafe - -## 0.107.3 - -### Patch Changes - -- Updated dependencies [6a5591a] - - @effect-app/schema@0.150.3 - -## 0.107.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.97.1 - - @effect-app/schema@0.150.2 - -## 0.107.1 - -### Patch Changes - -- fd520f7: chore: restore UnionBrand -- Updated dependencies [fd520f7] - - @effect-app/schema@0.150.1 - -## 0.107.0 - -### Minor Changes - -- 640d24a: reference schema2 -- 640d24a: update packages - -### Patch Changes - -- 640d24a: mm -- 640d24a: clean -- 640d24a: cleanup -- 640d24a: f -- 640d24a: update -- 640d24a: faker -- 640d24a: cleanup schema ext -- 640d24a: fix stringid and clientfor -- 640d24a: updates -- 640d24a: S -- 640d24a: fix things -- 640d24a: improve -- 640d24a: update schema -- 640d24a: upd -- 640d24a: cl -- 640d24a: form and client fixes -- 640d24a: update packages -- 640d24a: ext -- 640d24a: improve rc -- 640d24a: fix RequestId -- 640d24a: dynamic faker arb -- 640d24a: fix empty body -- 640d24a: fix mapPath should use From -- 640d24a: exports -- 640d24a: use schema fork -- 640d24a: cleanup errors and use decodeSyc over parseSync -- 640d24a: update packages -- 640d24a: expose ParseResult -- 640d24a: update packages -- 640d24a: fup -- 640d24a: upd -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] - - @effect-app/schema@0.150.0 - - @effect-app/core@0.97.0 - -## 0.107.0-next.45 - -### Patch Changes - -- d2bc3ac: update schema -- Updated dependencies [d2bc3ac] - - @effect-app/schema@0.150.0-next.30 - -## 0.107.0-next.44 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/schema@0.150.0-next.29 - - @effect-app/core@0.97.0-next.8 - -## 0.107.0-next.43 - -### Patch Changes - -- 19dc3b1: ext -- Updated dependencies [19dc3b1] - - @effect-app/schema@0.150.0-next.28 - -## 0.107.0-next.42 - -### Patch Changes - -- 09ed839: dynamic faker arb - -## 0.107.0-next.41 - -### Patch Changes - -- b6d4272: update packages -- Updated dependencies [0bc4207] -- Updated dependencies [b6d4272] - - @effect-app/schema@0.150.0-next.27 - - @effect-app/core@0.97.0-next.7 - -## 0.107.0-next.40 - -### Patch Changes - -- 14b33a9: cleanup errors and use decodeSyc over parseSync -- Updated dependencies [14b33a9] - - @effect-app/schema@0.150.0-next.26 - -## 0.107.0-next.39 - -### Patch Changes - -- Updated dependencies [aa1b654] - - @effect-app/schema@0.150.0-next.25 - -## 0.107.0-next.38 - -### Patch Changes - -- 64793a8: fix mapPath should use From - -## 0.107.0-next.37 - -### Patch Changes - -- 9e151ed: update packages -- Updated dependencies [9e151ed] - - @effect-app/schema@0.150.0-next.24 - - @effect-app/core@0.97.0-next.6 - -## 0.107.0-next.36 - -### Patch Changes - -- Updated dependencies [6b89c27] - - @effect-app/core@0.97.0-next.5 - - @effect-app/schema@0.150.0-next.23 - -## 0.107.0-next.35 - -### Patch Changes - -- 169c159: expose ParseResult -- Updated dependencies [169c159] - - @effect-app/schema@0.150.0-next.22 - -## 0.107.0-next.34 - -### Patch Changes - -- b46e30d: fup - -## 0.107.0-next.33 - -### Patch Changes - -- Updated dependencies [c57bb66] - - @effect-app/schema@0.150.0-next.21 - -## 0.107.0-next.32 - -### Patch Changes - -- 735dea9: improve rc - -## 0.107.0-next.31 - -### Patch Changes - -- 21482c6: cl - -## 0.107.0-next.30 - -### Patch Changes - -- eab24be: upd - -## 0.107.0-next.29 - -### Patch Changes - -- def06a7: fix things - -## 0.107.0-next.28 - -### Patch Changes - -- eb31797: fix empty body - -## 0.107.0-next.27 - -### Patch Changes - -- 65696a6: fix RequestId - -## 0.107.0-next.26 - -### Patch Changes - -- 777162f: improve -- Updated dependencies [777162f] - - @effect-app/schema@0.150.0-next.20 - -## 0.107.0-next.25 - -### Patch Changes - -- 0776693: fix stringid and clientfor - -## 0.107.0-next.24 - -### Patch Changes - -- 68eaf52: form and client fixes - -## 0.107.0-next.23 - -### Patch Changes - -- Updated dependencies [ca927a5] - - @effect-app/schema@0.150.0-next.19 - -## 0.107.0-next.22 - -### Patch Changes - -- 5f62f5a: upd -- Updated dependencies [5f62f5a] - - @effect-app/schema@0.150.0-next.18 - -## 0.107.0-next.21 - -### Patch Changes - -- Updated dependencies [53d4b3e] - - @effect-app/schema@0.150.0-next.17 - -## 0.107.0-next.20 - -### Patch Changes - -- Updated dependencies [63d4549] - - @effect-app/schema@0.150.0-next.16 - -## 0.107.0-next.19 - -### Patch Changes - -- b19ce70: S - -## 0.107.0-next.18 - -### Patch Changes - -- 92ff6b9: exports -- Updated dependencies [92ff6b9] - - @effect-app/schema@0.150.0-next.15 - -## 0.107.0-next.17 - -### Patch Changes - -- Updated dependencies [c234016] - - @effect-app/schema@0.150.0-next.14 - -## 0.107.0-next.16 - -### Patch Changes - -- Updated dependencies [5d53b26] - - @effect-app/schema@0.150.0-next.13 - -## 0.107.0-next.15 - -### Patch Changes - -- Updated dependencies [147bc14] - - @effect-app/schema@0.150.0-next.12 - -## 0.107.0-next.14 - -### Patch Changes - -- Updated dependencies [660d57a] - - @effect-app/schema@0.150.0-next.11 - -## 0.107.0-next.13 - -### Patch Changes - -- 20076e0: update -- Updated dependencies [20076e0] - - @effect-app/schema@0.150.0-next.10 - -## 0.107.0-next.12 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.97.0-next.4 - - @effect-app/schema@0.150.0-next.9 - -## 0.107.0-next.11 - -### Patch Changes - -- ac35466: use schema fork -- Updated dependencies [ac35466] - - @effect-app/schema@0.150.0-next.8 - -## 0.107.0-next.10 - -### Patch Changes - -- Updated dependencies [4f8ade3] - - @effect-app/core@0.97.0-next.3 - - @effect-app/schema@0.150.0-next.7 - -## 0.107.0-next.9 - -### Patch Changes - -- Updated dependencies [8897586] - - @effect-app/schema@0.150.0-next.6 - -## 0.107.0-next.8 - -### Patch Changes - -- c550ee5: updates -- Updated dependencies [c550ee5] - - @effect-app/schema@0.150.0-next.5 - - @effect-app/core@0.97.0-next.2 - -## 0.107.0-next.7 - -### Patch Changes - -- 82b3ca0: mm - -## 0.107.0-next.6 - -### Patch Changes - -- Updated dependencies [ace7afb] - - @effect-app/schema@0.150.0-next.4 - -## 0.107.0-next.5 - -### Patch Changes - -- 3320864: cleanup schema ext -- Updated dependencies [3320864] - - @effect-app/schema@0.150.0-next.3 - -## 0.107.0-next.4 - -### Patch Changes - -- Updated dependencies [fde3e90] - - @effect-app/schema@0.150.0-next.2 - - @effect-app/core@0.97.0-next.1 - -## 0.107.0-next.3 - -### Patch Changes - -- 3d9e1ab: clean -- 47d0151: faker - -## 0.107.0-next.2 - -### Patch Changes - -- c3de47e: f - -## 0.107.0-next.1 - -### Patch Changes - -- bd2130b: cleanup -- Updated dependencies [bd2130b] - - @effect-app/schema@0.150.0-next.1 - -## 0.107.0-next.0 - -### Minor Changes - -- 87a6ac2: update packages - -### Patch Changes - -- Updated dependencies [87a6ac2] - - @effect-app/schema2@0.1.0-next.0 - - @effect-app/core@0.97.0-next.0 - -## 0.106.1 - -### Patch Changes - -- efb854a: update packages -- Updated dependencies [efb854a] - - @effect-app/schema@0.90.1 - - @effect-app/core@0.96.1 - -## 0.106.0 - -### Minor Changes - -- 4330242: improve: TagClass - -## 0.105.0 - -### Minor Changes - -- c6efdeb: update packages - -### Patch Changes - -- Updated dependencies [c6efdeb] - - @effect-app/schema@0.90.0 - - @effect-app/core@0.96.0 - -## 0.104.1 - -### Patch Changes - -- cf6ae3c: update packages -- Updated dependencies [cf6ae3c] - - @effect-app/schema@0.89.1 - - @effect-app/core@0.95.1 - -## 0.104.0 - -### Minor Changes - -- 46bd7be: update packages - -### Patch Changes - -- Updated dependencies [46bd7be] - - @effect-app/schema@0.89.0 - - @effect-app/core@0.95.0 - -## 0.103.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.94.2 - - @effect-app/schema@0.88.3 - -## 0.103.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.94.1 - - @effect-app/schema@0.88.2 - -## 0.103.0 - -### Minor Changes - -- 92d7e37: update packages - -### Patch Changes - -- Updated dependencies [92d7e37] -- Updated dependencies - - @effect-app/core@0.94.0 - - @effect-app/schema@0.88.1 - -## 0.102.0 - -### Minor Changes - -- bump - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.93.0 - - @effect-app/schema@0.88.0 - -## 0.101.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.87.10 - -## 0.101.0 - -### Minor Changes - -- 67eb741: update packages - -### Patch Changes - -- Updated dependencies [67eb741] - - @effect-app/core@0.92.0 - - @effect-app/schema@0.87.9 - -## 0.100.1 - -### Patch Changes - -- 7f447d3: update packages -- Updated dependencies [7f447d3] - - @effect-app/core@0.91.1 - - @effect-app/schema@0.87.8 - -## 0.100.0 - -### Minor Changes - -- ad8154a: update packages - -### Patch Changes - -- Updated dependencies [ad8154a] - - @effect-app/core@0.91.0 - - @effect-app/schema@0.87.7 - -## 0.99.1 - -### Patch Changes - -- f33d0a1: feat: change request path to extends from `/${string}` for platform alignment -- Updated dependencies [f33d0a1] - - @effect-app/schema@0.87.6 - -## 0.99.0 - -### Minor Changes - -- f20991e: update packages - -### Patch Changes - -- Updated dependencies [f20991e] - - @effect-app/core@0.90.0 - - @effect-app/schema@0.87.5 - -## 0.98.3 - -### Patch Changes - -- Updated dependencies [687c313] - - @effect-app/schema@0.87.4 - -## 0.98.2 - -### Patch Changes - -- Updated dependencies [fcfea25] - - @effect-app/schema@0.87.3 - -## 0.98.1 - -### Patch Changes - -- Updated dependencies [e468bd0] - - @effect-app/schema@0.87.2 - -## 0.98.0 - -### Minor Changes - -- 98fbdb3: update packages - -### Patch Changes - -- Updated dependencies [98fbdb3] - - @effect-app/core@0.89.0 - - @effect-app/schema@0.87.1 - -## 0.97.0 - -### Minor Changes - -- 8b90f25: feat: add NumberAsInt, PositiveNumberAsInt - -### Patch Changes - -- Updated dependencies [8b90f25] - - @effect-app/schema@0.87.0 - -## 0.96.2 - -### Patch Changes - -- c3c5877: extend refinements with copy and clone - -## 0.96.1 - -### Patch Changes - -- a5b77fc: fix types - -## 0.96.0 - -### Minor Changes - -- 60ecffb: feat: copy/clone trait trial - -### Patch Changes - -- Updated dependencies [60ecffb] - - @effect-app/schema@0.86.0 - -## 0.95.0 - -### Minor Changes - -- fb332b4: update packages - -### Patch Changes - -- Updated dependencies [fb332b4] - - @effect-app/schema@0.85.0 - - @effect-app/core@0.88.0 - -## 0.94.0 - -### Minor Changes - -- 0069d09: Cleanup layers - -## 0.93.0 - -### Minor Changes - -- a681ab6: update packages - -### Patch Changes - -- Updated dependencies [a681ab6] - - @effect-app/schema@0.84.0 - - @effect-app/core@0.87.0 - -## 0.92.0 - -### Minor Changes - -- a9e8b8f: update packages - -### Patch Changes - -- Updated dependencies [169ccb0] -- Updated dependencies [a9e8b8f] - - @effect-app/core@0.86.0 - - @effect-app/schema@0.83.0 - -## 0.91.1 - -### Patch Changes - -- Updated dependencies [3cf98d6] - - @effect-app/schema@0.82.2 - -## 0.91.0 - -### Minor Changes - -- e0b2e24: fix: update for Secret - -### Patch Changes - -- Updated dependencies [e0b2e24] - - @effect-app/core@0.85.0 - - @effect-app/schema@0.82.1 - -## 0.90.0 - -### Minor Changes - -- ac36723: update dependencies - -### Patch Changes - -- Updated dependencies [ac36723] - - @effect-app/schema@0.82.0 - - @effect-app/core@0.84.0 - -## 0.89.0 - -### Minor Changes - -- da80c66: feat: add literalNumber - -### Patch Changes - -- Updated dependencies [da80c66] - - @effect-app/schema@0.81.0 - -## 0.88.9 - -### Patch Changes - -- Updated dependencies [8235a1a] - - @effect-app/core@0.83.3 - - @effect-app/schema@0.80.6 - -## 0.88.8 - -### Patch Changes - -- Updated dependencies [f6e1e15] - - @effect-app/schema@0.80.5 - -## 0.88.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.83.2 - - @effect-app/schema@0.80.4 - -## 0.88.6 - -### Patch Changes - -- Updated dependencies [8a81a8f] - - @effect-app/core@0.83.1 - - @effect-app/schema@0.80.3 - -## 0.88.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.83.0 - - @effect-app/schema@0.80.2 - -## 0.88.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.82.1 - - @effect-app/schema@0.80.1 - -## 0.88.3 - -### Patch Changes - -- Updated dependencies [4012f97] - - @effect-app/schema@0.80.0 - - @effect-app/core@0.82.0 - -## 0.88.2 - -### Patch Changes - -- 7f946cb: remove obsolete helpers -- Updated dependencies [7f946cb] - - @effect-app/core@0.81.1 - - @effect-app/schema@0.79.1 - -## 0.88.1 - -### Patch Changes - -- 65886f2: fix type sigs - -## 0.88.0 - -### Minor Changes - -- 16ffb5e: class service/repo support andThen, drop flatMap - -## 0.87.0 - -### Minor Changes - -- align v - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.81.0 - - @effect-app/schema@0.79.0 - -## 0.86.0 - -### Minor Changes - -- align v - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.80.0 - - @effect-app/schema@0.78.0 - -## 0.85.5 - -### Patch Changes - -- 4627526: improve string name - -## 0.85.4 - -### Patch Changes - -- Updated dependencies [081418b] - - @effect-app/schema@0.77.1 - -## 0.85.3 - -### Patch Changes - -- fix - -## 0.85.2 - -### Patch Changes - -- fix - -## 0.85.1 - -### Patch Changes - -- fix typo - -## 0.85.0 - -### Minor Changes - -- feat: support constructor input for clientFor requests - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.77.0 - -## 0.84.0 - -### Minor Changes - -- improve String schemas - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.76.0 - -## 0.83.0 - -### Minor Changes - -- fa908e8: move fluent-extensions - -## 0.82.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.75.3 - -## 0.82.2 - -### Patch Changes - -- Updated dependencies [086c2b1] - - @effect-app/schema@0.75.2 - -## 0.82.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.75.1 - -## 0.82.0 - -### Minor Changes - -- 68359de: more schema alignment - -### Patch Changes - -- Updated dependencies [68359de] - - @effect-app/schema@0.75.0 - -## 0.81.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.74.2 - -## 0.81.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.74.1 - -## 0.81.0 - -### Minor Changes - -- 771d7e6: Fix model name - -### Patch Changes - -- Updated dependencies [771d7e6] - - @effect-app/schema@0.74.0 - -## 0.80.1 - -### Patch Changes - -- Updated dependencies [af19954] - - @effect-app/core@0.79.1 - - @effect-app/schema@0.73.1 - -## 0.80.0 - -### Minor Changes - -- 1f6b9a1: update packages - -### Patch Changes - -- Updated dependencies [1f6b9a1] - - @effect-app/schema@0.73.0 - - @effect-app/core@0.79.0 - -## 0.79.0 - -### Minor Changes - -- c528c33: initial alignment of Schema with effect/schema - -### Patch Changes - -- Updated dependencies [c528c33] - - @effect-app/schema@0.72.0 - - @effect-app/core@0.78.0 - -## 0.78.2 - -### Patch Changes - -- d4a6be2: fix ext - -## 0.78.1 - -### Patch Changes - -- 4c9e60a: add basic fluent extensions for projects without tsplus - -## 0.78.0 - -### Minor Changes - -- da85f48: cleanup operators. add nonEmpty map variant - -### Patch Changes - -- ab7eddc: update packages -- Updated dependencies [ab7eddc] -- Updated dependencies [da85f48] - - @effect-app/core@0.77.0 - - @effect-app/schema@0.71.0 - -## 0.77.3 - -### Patch Changes - -- Updated dependencies [d462c64] - - @effect-app/schema@0.70.0 - - @effect-app/core@0.76.0 - -## 0.77.2 - -### Patch Changes - -- Updated dependencies [26a3bc8] - - @effect-app/schema@0.69.2 - -## 0.77.1 - -### Patch Changes - -- Updated dependencies [9efeed84] - - @effect-app/core@0.75.1 - - @effect-app/schema@0.69.1 - -## 0.77.0 - -### Minor Changes - -- 88f5695c: revert schema classes double as tag - -### Patch Changes - -- Updated dependencies [88f5695c] - - @effect-app/schema@0.69.0 - - @effect-app/core@0.75.0 - -## 0.76.0 - -### Minor Changes - -- 46719cec: feat: schema classes double as Tag - -### Patch Changes - -- Updated dependencies [46719cec] - - @effect-app/schema@0.68.0 - - @effect-app/core@0.74.0 - -## 0.75.2 - -### Patch Changes - -- Updated dependencies [5813c6b2] - - @effect-app/core@0.73.1 - - @effect-app/schema@0.67.2 - -## 0.75.1 - -### Patch Changes - -- a644e7b5: fix methods exports -- Updated dependencies [a644e7b5] - - @effect-app/schema@0.67.1 - -## 0.75.0 - -### Minor Changes - -- 6c180253: cleanup - -### Patch Changes - -- Updated dependencies [6c180253] - - @effect-app/schema@0.67.0 - - @effect-app/core@0.73.0 - -## 0.74.4 - -### Patch Changes - -- Updated dependencies [f436ce54] - - @effect-app/schema@0.66.0 - -## 0.74.3 - -### Patch Changes - -- 7ff1f6f9: update packages -- 04f6bafa: update packages -- Updated dependencies [7ff1f6f9] -- Updated dependencies [04f6bafa] - - @effect-app/core@0.72.1 - - @effect-app/schema@0.65.3 - -## 0.74.2 - -### Patch Changes - -- Updated dependencies [07c0dbfc] - - @effect-app/schema@0.65.2 - -## 0.74.1 - -### Patch Changes - -- Updated dependencies [a7a9f51f] - - @effect-app/schema@0.65.1 - -## 0.74.0 - -### Minor Changes - -- 0a4bfbc7: update packages - -### Patch Changes - -- Updated dependencies [0a4bfbc7] - - @effect-app/schema@0.65.0 - - @effect-app/core@0.72.0 - -## 0.73.5 - -### Patch Changes - -- 56583d9b: align span names -- Updated dependencies [baf5a2b7] - - @effect-app/core@0.71.5 - - @effect-app/schema@0.64.5 - -## 0.73.4 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/core@0.71.4 - - @effect-app/schema@0.64.4 - -## 0.73.3 - -### Patch Changes - -- Updated dependencies [99205b44] - - @effect-app/core@0.71.3 - - @effect-app/schema@0.64.3 - -## 0.73.2 - -### Patch Changes - -- 002cdb31: update packages -- Updated dependencies [002cdb31] - - @effect-app/core@0.71.2 - - @effect-app/schema@0.64.2 - -## 0.73.1 - -### Patch Changes - -- Updated dependencies [868919c8] - - @effect-app/schema@0.64.1 - - @effect-app/core@0.71.1 - -## 0.73.0 - -### Minor Changes - -- 90af6a41: update packages - -### Patch Changes - -- Updated dependencies [90af6a41] - - @effect-app/schema@0.64.0 - - @effect-app/core@0.71.0 - -## 0.72.9 - -### Patch Changes - -- de419614: enhance errors - -## 0.72.8 - -### Patch Changes - -- ed346253: Improve NotLoggedInError - -## 0.72.7 - -### Patch Changes - -- ff54e102: update packages -- Updated dependencies [ff54e102] - - @effect-app/core@0.70.2 - - @effect-app/schema@0.63.2 - -## 0.72.6 - -### Patch Changes - -- ef626697: improve client span - -## 0.72.5 - -### Patch Changes - -- 60615760: cleanup - -## 0.72.4 - -### Patch Changes - -- fix id - -## 0.72.3 - -### Patch Changes - -- f03b6b48: fix - -## 0.72.2 - -### Patch Changes - -- Updated dependencies [3f0dba21] - - @effect-app/core@0.70.1 - - @effect-app/schema@0.63.1 - -## 0.72.1 - -### Patch Changes - -- 8b0e01b4: workaround new Ref variance - -## 0.72.0 - -### Minor Changes - -- 283ebbe7: update packages - -### Patch Changes - -- Updated dependencies [283ebbe7] - - @effect-app/schema@0.63.0 - - @effect-app/core@0.70.0 - -## 0.71.1 - -### Patch Changes - -- d1a6193b: feat: support span across client and api -- Updated dependencies [d1a6193b] - - @effect-app/core@0.69.1 - - @effect-app/schema@0.62.5 - -## 0.71.0 - -### Minor Changes - -- 40a6c3b6: update packages - -### Patch Changes - -- Updated dependencies [40a6c3b6] - - @effect-app/core@0.69.0 - - @effect-app/schema@0.62.4 - -## 0.70.4 - -### Patch Changes - -- housekeeping: update packages -- Updated dependencies - - @effect-app/core@0.68.3 - - @effect-app/schema@0.62.3 - -## 0.70.3 - -### Patch Changes - -- e4185126: unification workaround - -## 0.70.2 - -### Patch Changes - -- 263a56b9: update packages -- Updated dependencies [263a56b9] - - @effect-app/core@0.68.2 - - @effect-app/schema@0.62.2 - -## 0.70.1 - -### Patch Changes - -- aa3c98d1: update packages -- Updated dependencies [aa3c98d1] - - @effect-app/schema@0.62.1 - - @effect-app/core@0.68.1 - -## 0.70.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.68.0 - - @effect-app/schema@0.62.0 - -## 0.69.10 - -### Patch Changes - -- Updated dependencies [9c347b64] - - @effect-app/schema@0.61.1 - -## 0.69.9 - -### Patch Changes - -- Updated dependencies [fdd19b0b] - - @effect-app/schema@0.61.0 - -## 0.69.8 - -### Patch Changes - -- d7dd36d4: update packages -- Updated dependencies [d7dd36d4] - - @effect-app/schema@0.60.5 - - @effect-app/core@0.67.5 - -## 0.69.7 - -### Patch Changes - -- 780a6718: update packages -- Updated dependencies [780a6718] - - @effect-app/core@0.67.4 - - @effect-app/schema@0.60.4 - -## 0.69.6 - -### Patch Changes - -- 57f506e7: feat: add demandJson for HttpClient - -## 0.69.5 - -### Patch Changes - -- 8c0b8270: improve json parse error - -## 0.69.4 - -### Patch Changes - -- 84e54d54: Update packages -- Updated dependencies [84e54d54] - - @effect-app/core@0.67.3 - - @effect-app/schema@0.60.3 - -## 0.69.3 - -### Patch Changes - -- 696a525e: update packages -- Updated dependencies [696a525e] - - @effect-app/schema@0.60.2 - - @effect-app/core@0.67.2 - -## 0.69.2 - -### Patch Changes - -- f8cb0efd: schemaJsonBody - -## 0.69.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.67.1 - - @effect-app/schema@0.60.1 - -## 0.69.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.60.0 - - @effect-app/core@0.67.0 - -## 0.68.3 - -### Patch Changes - -- blurg - -## 0.68.2 - -### Patch Changes - -- 7118bf61: fix error type - -## 0.68.1 - -### Patch Changes - -- Updated dependencies [56a4418f] - - @effect-app/schema@0.59.0 - -## 0.68.0 - -### Minor Changes - -- 271e2c7a: yieldable errors - -### Patch Changes - -- Updated dependencies [271e2c7a] - - @effect-app/schema@0.58.0 - - @effect-app/core@0.66.0 - -## 0.67.10 - -### Patch Changes - -- Updated dependencies [89bae16e] - - @effect-app/core@0.65.5 - - @effect-app/schema@0.57.10 - -## 0.67.9 - -### Patch Changes - -- 838eeb10: update packages -- Updated dependencies [838eeb10] - - @effect-app/schema@0.57.9 - -## 0.67.8 - -### Patch Changes - -- Updated dependencies [0b279a80] - - @effect-app/core@0.65.4 - - @effect-app/schema@0.57.8 - -## 0.67.7 - -### Patch Changes - -- 50c7fe31: fix typo - -## 0.67.6 - -### Patch Changes - -- 7fc3cfbe: improve client api - -## 0.67.5 - -### Patch Changes - -- 26087368: update platform extensions -- Updated dependencies [26087368] - - @effect-app/core@0.65.3 - - @effect-app/schema@0.57.7 - -## 0.67.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.65.2 - - @effect-app/schema@0.57.6 - -## 0.67.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.65.1 - - @effect-app/schema@0.57.5 - -## 0.67.2 - -### Patch Changes - -- fix HttpClient global - -## 0.67.1 - -### Patch Changes - -- 924817d9: fix type - -## 0.67.0 - -### Minor Changes - -- 51b546c5: feat: Convert clientFor to use @effect/platform HttpClient - -### Patch Changes - -- Updated dependencies [51b546c5] - - @effect-app/core@0.65.0 - - @effect-app/schema@0.57.4 - -## 0.66.0 - -### Minor Changes - -- 5f3ae060: feat: add HttpClient from @effect/platform - -### Patch Changes - -- Updated dependencies [5f3ae060] - - @effect-app/core@0.64.0 - - @effect-app/schema@0.57.3 - -## 0.65.2 - -### Patch Changes - -- 87e29b7d: export httpclient -- Updated dependencies [87e29b7d] - - @effect-app/core@0.63.2 - - @effect-app/schema@0.57.2 - -## 0.65.1 - -### Patch Changes - -- fix tsplus.config for pnpm -- Updated dependencies - - @effect-app/core@0.63.1 - - @effect-app/schema@0.57.1 - -## 0.65.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.57.0 - - @effect-app/core@0.63.0 - -## 0.64.0 - -### Minor Changes - -- update packages, improve error reporting - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.62.0 - - @effect-app/schema@0.56.0 - -## 0.63.1 - -### Patch Changes - -- add date extensions - -## 0.63.0 - -### Minor Changes - -- add match and update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.55.0 - - @effect-app/core@0.61.0 - -## 0.62.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.54.0 - - @effect-app/core@0.60.0 - -## 0.61.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.53.0 - - @effect-app/core@0.59.0 - -## 0.60.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.58.4 - - @effect-app/schema@0.52.4 - -## 0.60.3 - -### Patch Changes - -- Updated dependencies [6b002041] -- Updated dependencies - - @effect-app/core@0.58.3 - - @effect-app/schema@0.52.3 - -## 0.60.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.58.2 - - @effect-app/schema@0.52.2 - -## 0.60.1 - -### Patch Changes - -- improve tsc parsing performance 3x -- Updated dependencies - - @effect-app/core@0.58.1 - - @effect-app/schema@0.52.1 - -## 0.60.0 - -### Minor Changes - -- 4d2e68df: Switch to effect meta package - -### Patch Changes - -- Updated dependencies [4d2e68df] - - @effect-app/schema@0.52.0 - - @effect-app/core@0.58.0 - -## 0.59.7 - -### Patch Changes - -- fix tagclass type - -## 0.59.6 - -### Patch Changes - -- fix tagclass - -## 0.59.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.5 - - @effect-app/schema@0.51.5 - -## 0.59.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.4 - - @effect-app/schema@0.51.4 - -## 0.59.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.3 - - @effect-app/schema@0.51.3 - -## 0.59.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.2 - - @effect-app/schema@0.51.2 - -## 0.59.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.1 - - @effect-app/schema@0.51.1 - -## 0.59.0 - -### Minor Changes - -- c80b9e7a: Updated to latest Effect - -### Patch Changes - -- Updated dependencies [c80b9e7a] - - @effect-app/schema@0.51.0 - - @effect-app/core@0.57.0 - -## 0.58.2 - -### Patch Changes - -- fix type of E variants of clientFor - -## 0.58.1 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/core@0.56.1 - - @effect-app/schema@0.50.1 - -## 0.58.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.50.0 - - @effect-app/core@0.56.0 - -## 0.57.0 - -### Minor Changes - -- aa9c3126: using the new faker-js package instead of the old cursed one - -## 0.56.1 - -### Patch Changes - -- e4609777: Use distributive omit - -## 0.56.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.55.0 - - @effect-app/schema@0.49.0 - -## 0.55.2 - -### Patch Changes - -- add pick and omit object extensions - -## 0.55.1 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.48.1 - - @effect-app/core@0.54.1 - -## 0.55.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.54.0 - - @effect-app/schema@0.48.0 - -## 0.54.0 - -### Minor Changes - -- Cleanup legacy cache - -## 0.53.2 - -### Patch Changes - -- EffectCache - -## 0.53.1 - -### Patch Changes - -- Add exports -- Updated dependencies - - @effect-app/core@0.53.1 - - @effect-app/schema@0.47.1 - -## 0.53.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.53.0 - - @effect-app/schema@0.47.0 - -## 0.52.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.52.0 - - @effect-app/schema@0.46.0 - -## 0.51.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.45.0 - - @effect-app/core@0.51.0 - -## 0.50.1 - -### Patch Changes - -- c78a54ff: Enhance TagClass - -## 0.50.0 - -### Minor Changes - -- update ids - -## 0.49.1 - -### Patch Changes - -- Add brandedStringId - -## 0.49.0 - -### Minor Changes - -- a - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @effect-app/schema@0.44.0 - - @effect-app/core@0.50.0 - -## 0.48.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.49.1 - - @effect-app/schema@0.43.3 - -## 0.48.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.43.2 - -## 0.48.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.43.1 - -## 0.48.0 - -### Minor Changes - -- Compiler test - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.49.0 - - @effect-app/schema@0.43.0 - -## 0.47.0 - -### Minor Changes - -- Update depdendencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.42.0 - - @effect-app/core@0.48.0 - -## 0.46.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.2 - -## 0.46.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.1 - -## 0.46.0 - -### Minor Changes - -- Cleanup schema ext location - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.0 - -## 0.45.0 - -### Minor Changes - -- Improve codegen - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.40.0 - -## 0.44.12 - -### Patch Changes - -- fix rest -- Updated dependencies - - @effect-app/schema@0.39.12 - -## 0.44.11 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.11 - -## 0.44.10 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.10 - -## 0.44.9 - -### Patch Changes - -- feat: property extensions -- Updated dependencies - - @effect-app/schema@0.39.9 - -## 0.44.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.8 - -## 0.44.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.7 - -## 0.44.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.6 - -## 0.44.5 - -### Patch Changes - -- fix -- Updated dependencies - - @effect-app/schema@0.39.5 - -## 0.44.4 - -### Patch Changes - -- fix global -- Updated dependencies - - @effect-app/core@0.47.1 - - @effect-app/schema@0.39.4 - -## 0.44.3 - -### Patch Changes - -- Improve schema prop api -- Updated dependencies - - @effect-app/schema@0.39.3 - -## 0.44.2 - -### Patch Changes - -- Improve withDefadult -- Updated dependencies - - @effect-app/schema@0.39.2 - -## 0.44.1 - -### Patch Changes - -- fix withDefault -- Updated dependencies - - @effect-app/schema@0.39.1 - -## 0.44.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.0 - - @effect-app/core@0.47.0 - -## 0.43.0 - -### Minor Changes - -- Improve Schema withDefault - -## 0.42.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.46.0 - - @effect-app/schema@0.38.0 - -## 0.41.0 - -### Minor Changes - -- Update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.37.0 - - @effect-app/core@0.45.0 - -## 0.40.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.44.0 - - @effect-app/schema@0.36.1 - -## 0.40.0 - -### Minor Changes - -- support optional constructors for schema with none or only optional input. - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.36.0 - -## 0.39.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.35.0 - - @effect-app/core@0.43.0 - -## 0.38.0 - -### Minor Changes - -- update packages and cleanup - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.34.0 - - @effect-app/core@0.42.0 - -## 0.37.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.33.0 - - @effect-app/core@0.41.0 - -## 0.36.1 - -### Patch Changes - -- fix error channel of allLowerFirst - -## 0.36.0 - -### Minor Changes - -- AllLowerFirst should be Par - -## 0.35.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.40.0 - - @effect-app/schema@0.32.0 - -## 0.34.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.39.1 - - @effect-app/schema@0.31.1 - -## 0.34.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies -- Updated dependencies [ba4849c9] - - @effect-app/schema@0.31.0 - - @effect-app/core@0.39.0 - -## 0.33.4 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.30.2 - - @effect-app/core@0.38.1 - -## 0.33.3 - -### Patch Changes - -- fup - -## 0.33.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.30.1 - -## 0.33.1 - -### Patch Changes - -- fup - -## 0.33.0 - -### Minor Changes - -- improve min/max/range for numbers. - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.30.0 - -## 0.32.0 - -### Minor Changes - -- Properly identify number schemas - -## 0.31.0 - -### Minor Changes - -- Update packages -- feat(form): number vs string - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.29.0 - - @effect-app/core@0.38.0 - -## 0.30.2 - -### Patch Changes - -- FUP - -## 0.30.1 - -### Patch Changes - -- rename: servicesOrEffects to allLowerFirst - -## 0.30.0 - -### Minor Changes - -- Update to latest effect - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.28.0 - - @effect-app/core@0.37.0 - -## 0.29.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.36.1 - - @effect-app/schema@0.27.1 - -## 0.29.1 - -### Patch Changes - -- Extend Services extensions - -## 0.29.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.27.0 - - @effect-app/core@0.36.0 - -## 0.28.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.26.0 - - @effect-app/core@0.35.0 - -## 0.27.1 - -### Patch Changes - -- fix dep -- Updated dependencies - - @effect-app/schema@0.25.1 - -## 0.27.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.25.0 - - @effect-app/core@0.34.0 - -## 0.26.2 - -### Patch Changes - -- fix clientFor E - -## 0.26.1 - -### Patch Changes - -- fix - -## 0.26.0 - -### Minor Changes - -- Update packages, add support for class features to schema and optic - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.33.0 - - @effect-app/schema@0.24.0 - -## 0.25.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.32.3 - - @effect-app/schema@0.23.3 - -## 0.25.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.32.2 - - @effect-app/schema@0.23.2 - -## 0.25.1 - -### Patch Changes - -- Cleanup - -## 0.25.0 - -### Minor Changes - -- Improve Encoded codegen - -## 0.24.2 - -### Patch Changes - -- Fix compilation -- Updated dependencies - - @effect-app/core@0.32.1 - - @effect-app/schema@0.23.1 - -## 0.24.1 - -### Patch Changes - -- fix svc tag - -## 0.24.0 - -### Minor Changes - -- Update tsplus with companion instance extensions - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.23.0 - - @effect-app/core@0.32.0 - -## 0.23.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.31.0 - - @effect-app/schema@0.22.4 - -## 0.23.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.30.3 - - @effect-app/schema@0.22.3 - -## 0.23.5 - -### Patch Changes - -- fix service tag -- Updated dependencies - - @effect-app/core@0.30.2 - - @effect-app/schema@0.22.2 - -## 0.23.4 - -### Patch Changes - -- tag workaround 3 - -## 0.23.3 - -### Patch Changes - -- Workaround tag - -## 0.23.2 - -### Patch Changes - -- tag workaround -- Updated dependencies - - @effect-app/core@0.30.1 - - @effect-app/schema@0.22.1 - -## 0.23.1 - -### Patch Changes - -- Fix service tag - -## 0.23.0 - -### Minor Changes - -- Update Effect - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.30.0 - - @effect-app/schema@0.22.0 - -## 0.22.5 - -### Patch Changes - -- Updated dependencies [00d34a69] - - @effect-app/schema@0.21.0 - -## 0.22.4 - -### Patch Changes - -- Improve curry support for PreparedLens - -## 0.22.3 - -### Patch Changes - -- Fix - -## 0.22.2 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.20.1 - - @effect-app/core@0.29.1 - -## 0.22.1 - -### Patch Changes - -- d89f88cb: add assignTag, TagClass, ServiceTaggedClass - -## 0.22.0 - -### Minor Changes - -- b967055c: Replace fp-ts/core with effect/data - -### Patch Changes - -- Updated dependencies [b967055c] - - @effect-app/schema@0.20.0 - - @effect-app/core@0.29.0 - -## 0.21.14 - -### Patch Changes - -- 28d5530b: Fix Encoded - -## 0.21.13 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.28.0 - - @effect-app/schema@0.19.4 - -## 0.21.12 - -### Patch Changes - -- Updated dependencies [39da5db8] - - @effect-app/core@0.27.0 - - @effect-app/schema@0.19.3 - -## 0.21.11 - -### Patch Changes - -- Updated dependencies [4679321f] - - @effect-app/core@0.26.0 - - @effect-app/schema@0.19.2 - -## 0.21.10 - -### Patch Changes - -- Updated dependencies [d66406c9] - - @effect-app/core@0.25.1 - - @effect-app/schema@0.19.1 - -## 0.21.9 - -### Patch Changes - -- Updated dependencies [b385e928] - - @effect-app/schema@0.19.0 - - @effect-app/core@0.25.0 - -## 0.21.8 - -### Patch Changes - -- Updated dependencies [c4b4a20a] - - @effect-app/core@0.24.1 - - @effect-app/schema@0.18.7 - -## 0.21.7 - -### Patch Changes - -- Updated dependencies [3d49bb4a] - - @effect-app/schema@0.18.6 - -## 0.21.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.24.0 - - @effect-app/schema@0.18.5 - -## 0.21.5 - -### Patch Changes - -- Updated dependencies [8da4c8fc] - - @effect-app/core@0.23.1 - - @effect-app/schema@0.18.4 - -## 0.21.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.23.0 - - @effect-app/schema@0.18.3 - -## 0.21.3 - -### Patch Changes - -- c29ab9fa: default Effect.servicesWith\* to lowercase first letter services. - -## 0.21.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.22.2 - - @effect-app/schema@0.18.2 - -## 0.21.1 - -### Patch Changes - -- Updated dependencies [df13134f] - - @effect-app/core@0.22.1 - - @effect-app/schema@0.18.1 - -## 0.21.0 - -### Minor Changes - -- Update annotations, drop Aspects for now - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.22.0 - - @effect-app/schema@0.18.0 - -## 0.20.3 - -### Patch Changes - -- Updated dependencies [e3e768a5] - - @effect-app/core@0.21.3 - - @effect-app/schema@0.17.3 - -## 0.20.2 - -### Patch Changes - -- Updated dependencies [d9751183] - - @effect-app/core@0.21.2 - - @effect-app/schema@0.17.2 - -## 0.20.1 - -### Patch Changes - -- Updated dependencies [46d8b7e1] - - @effect-app/core@0.21.1 - - @effect-app/schema@0.17.1 - -## 0.20.0 - -### Minor Changes - -- 338f1f19: Update packages - -### Patch Changes - -- Updated dependencies [338f1f19] - - @effect-app/schema@0.17.0 - - @effect-app/core@0.21.0 - -## 0.19.3 - -### Patch Changes - -- Updated dependencies [00ac8993] - - @effect-app/core@0.20.2 - - @effect-app/schema@0.16.3 - -## 0.19.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.20.1 - - @effect-app/schema@0.16.2 - -## 0.19.1 - -### Patch Changes - -- 02997e8f: FUP -- Updated dependencies [02997e8f] - - @effect-app/schema@0.16.1 - -## 0.19.0 - -### Minor Changes - -- 4d49f303: Upgrade to @effect/data - -### Patch Changes - -- Updated dependencies [4d49f303] - - @effect-app/schema@0.16.0 - - @effect-app/core@0.20.0 - -## 0.18.2 - -### Patch Changes - -- ca8b9a4e: Fix Function flow - -## 0.18.1 - -### Patch Changes - -- Updated dependencies [f8d8e4ec] - - @effect-app/core@0.19.0 - - @effect-app/schema@0.15.6 - -## 0.18.0 - -### Minor Changes - -- Update annotations and leverage ForceLazy on Effect \_\_call - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.18.0 - - @effect-app/schema@0.15.5 - -## 0.17.5 - -### Patch Changes - -- 912ba66c: Update packages -- Updated dependencies [912ba66c] - - @effect-app/schema@0.15.4 - - @effect-app/core@0.17.4 - -## 0.17.4 - -### Patch Changes - -- Updated dependencies [407cc13e] - - @effect-app/core@0.17.3 - - @effect-app/schema@0.15.3 - -## 0.17.3 - -### Patch Changes - -- e9e498ee: Update packages -- Updated dependencies [e9e498ee] - - @effect-app/schema@0.15.2 - - @effect-app/core@0.17.2 - -## 0.17.2 - -### Patch Changes - -- 33d2a60f: Cleanup - -## 0.17.1 - -### Patch Changes - -- Updated dependencies [8b10e403] - - @effect-app/core@0.17.1 - - @effect-app/schema@0.15.1 - -## 0.17.0 - -### Minor Changes - -- 9f9b2d32: Update effect and fp-ts - -### Patch Changes - -- Updated dependencies [9f9b2d32] - - @effect-app/schema@0.15.0 - - @effect-app/core@0.17.0 - -## 0.16.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.16.1 - - @effect-app/schema@0.14.1 - -## 0.16.0 - -### Minor Changes - -- 67716050: Update to TS5 beta - -### Patch Changes - -- Updated dependencies [67716050] - - @effect-app/schema@0.14.0 - - @effect-app/core@0.16.0 - -## 0.15.1 - -### Patch Changes - -- 5a69e08a: Reclaim Duration -- Updated dependencies [5a69e08a] - - @effect-app/core@0.15.1 - - @effect-app/schema@0.13.1 - -## 0.15.0 - -### Minor Changes - -- 25ec7aff: Reclaim the Option - -### Patch Changes - -- Updated dependencies [25ec7aff] - - @effect-app/schema@0.13.0 - - @effect-app/core@0.15.0 - -## 0.14.2 - -### Patch Changes - -- Updated dependencies [90d6e600] - - @effect-app/core@0.14.2 - - @effect-app/schema@0.12.9 - -## 0.14.1 - -### Patch Changes - -- Updated dependencies [8f97d979] - - @effect-app/core@0.14.1 - - @effect-app/schema@0.12.8 - -## 0.14.0 - -### Minor Changes - -- e4730778: Test dual api for Optic - -### Patch Changes - -- Updated dependencies [e4730778] - - @effect-app/core@0.14.0 - - @effect-app/schema@0.12.7 - -## 0.13.0 - -### Minor Changes - -- e7b4a0b9: Cleanup extensions - -### Patch Changes - -- Updated dependencies [e7b4a0b9] - - @effect-app/core@0.13.0 - - @effect-app/schema@0.12.6 - -## 0.12.5 - -### Patch Changes - -- Updated dependencies [a1bbd31f] - - @effect-app/schema@0.12.5 - -## 0.12.4 - -### Patch Changes - -- Updated dependencies [ffd11b1c] - - @effect-app/schema@0.12.4 - - @effect-app/core@0.12.4 - -## 0.12.3 - -### Patch Changes - -- 602ffbce: Update packages -- Updated dependencies [602ffbce] - - @effect-app/schema@0.12.3 - - @effect-app/core@0.12.3 - -## 0.12.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.12.2 - - @effect-app/schema@0.12.2 - -## 0.12.1 - -### Patch Changes - -- Disable trace -- Updated dependencies - - @effect-app/schema@0.12.1 - - @effect-app/core@0.12.1 - -## 0.12.0 - -### Minor Changes - -- Update to effect and fp-ts 0.1 line - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.12.0 - - @effect-app/core@0.12.0 - -## 0.11.6 - -### Patch Changes - -- Fix ServiceBus should inherit runtime context - -## 0.11.5 - -### Patch Changes - -- e9a667fe: Add Operations - -## 0.11.4 - -### Patch Changes - -- 387dea6e: Fix Cache MapKey - -## 0.11.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.3 - - @effect-app/schema@0.11.3 - -## 0.11.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.2 - - @effect-app/schema@0.11.2 - -## 0.11.1 - -### Patch Changes - -- 5ee19e1e: Improve exports -- Updated dependencies [5ee19e1e] - - @effect-app/core@0.11.1 - - @effect-app/schema@0.11.1 - -## 0.11.0 - -### Minor Changes - -- Remove @effect-ts/\* and replace monocle with fp-ts/optic - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.0 - - @effect-app/schema@0.11.0 - -## 0.10.2 - -### Patch Changes - -- Updated dependencies [cc4b37ea] - - @effect-app/core@0.10.2 - - @effect-app/schema@0.10.2 - -## 0.10.1 - -### Patch Changes - -- Updated dependencies [c4b95c0d] - - @effect-app/core@0.10.1 - - @effect-app/schema@0.10.1 - -## 0.10.0 - -### Minor Changes - -- Removed more of @effect-ts/core legacy - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.10.0 - - @effect-app/schema@0.10.0 - -## 0.9.0 - -### Minor Changes - -- 7d445f3e: Adopt Equivalence and Equal from @effect/data - -### Patch Changes - -- Updated dependencies [7d445f3e] - - @effect-app/schema@0.9.0 - - @effect-app/core@0.9.0 - -## 0.8.0 - -### Minor Changes - -- bba14672: Update to latest @effect/io and @effect/data - -### Patch Changes - -- Updated dependencies [bba14672] - - @effect-app/schema@0.8.0 - - @effect-app/core@0.8.0 - -## 0.7.1 - -### Patch Changes - -- Cleanup infra packages diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json deleted file mode 100644 index c33bee31b..000000000 --- a/packages/effect-app/package.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "name": "effect-app", - "version": "4.0.0-beta.332", - "license": "MIT", - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/effect-app" - }, - "dependencies": { - "@tsconfig/strictest": "^2.0.8", - "date-fns": "^4.4.0", - "nanoid": "^5.1.11", - "path-parser": "^6.1.0", - "query-string": "^9.4.0", - "ts-toolbelt": "^9.6.0", - "validator": "^13.15.35" - }, - "devDependencies": { - "@faker-js/faker": "^8.4.1", - "@types/node": "25.9.1", - "@types/validator": "^13.15.10", - "fast-check": "^4.9.0", - "typescript": "~6.0.3", - "vitest": "^4.1.7" - }, - "peerDependencies": { - "effect": "^4.0.1" - }, - "typesVersions": { - "*": { - "*": [ - "dist/*" - ] - } - }, - "exports": { - ".": { - "types": "./src/index.ts", - "default": "./src/index.ts" - }, - "./internal/*": null, - "./*": { - "types": "./src/*.ts", - "default": "./src/*.ts" - } - }, - "publishConfig": { - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./internal/*": null, - "./*": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - } - }, - "directory": ".publish", - "linkDirectory": false - }, - "scripts": { - "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && pnpm check && node ../../scripts/rewrite-dist-declaration-imports.mjs dist", - "check": "tsgo --build", - "build:tsc-src": "pnpm clean-dist && tsc --build ./tsconfig.src.json && node ../../scripts/rewrite-dist-declaration-imports.mjs dist", - "build:src": "pnpm build:tsc-src", - "build": "pnpm build:tsc", - "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsgo -w", - "clean": "rm -rf dist", - "clean-dist": "sh ../../scripts/clean-dist.sh", - "circular": "pnpm circular:src && pnpm circular:dist", - "circular:src": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", - "circular:dist": "madge --circular --extensions js ./dist", - "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsgo --noEmit", - "lint": "oxlint --type-aware ./src && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --type-aware --fix ./src && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "test": "vitest", - "test:run": "pnpm run test run --passWithNoTests", - "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", - "ncu": "ncu", - "pub": "pnpm publish --access public", - "prepack": "node ../../scripts/stagePublishPackage.mjs prepack", - "postpack": "node ../../scripts/stagePublishPackage.mjs postpack", - "postpublish": "node ../../scripts/stagePublishPackage.mjs postpublish" - }, - "gitHead": "bd8e27eea3eff97db8739d577d67e7336c078d28", - "sideEffects": [ - "./dist/faker.js", - "./faker", - "./src/faker", - "./dist/faker", - "./dist/src/faker.js", - "./dist/faker.js" - ] -} diff --git a/packages/effect-app/src/Array.ts b/packages/effect-app/src/Array.ts deleted file mode 100644 index a36c992c8..000000000 --- a/packages/effect-app/src/Array.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect/Array" -import * as Array from "effect/Array" -import * as Chunk from "effect/Chunk" -import * as Effect from "effect/Effect" -import { dual, type Predicate } from "./Function.ts" -import * as Option from "./Option.ts" - -export const toNonEmptyArray = Option.liftPredicate(Array.isReadonlyArrayNonEmpty) - -export const isArray: { - // uses ReadonlyArray here because otherwise the second overload don't work when ROA is involved. - (self: unknown): self is ReadonlyArray - (self: T): self is Extract> -} = Array.isArray - -export function NEAFromArray(ar: Array) { - return ar.length ? Option.some(ar as NonEmptyArray) : Option.none() -} - -export function NEROArrayFromArray(ar: ReadonlyArray) { - return ar.length ? Option.some(ar as NonEmptyReadonlyArray) : Option.none() -} - -export const groupByT = dual< - ( - f: (a: NoInfer) => Key - ) => (as: ReadonlyArray) => Array]>, - ( - as: ReadonlyArray, - f: (a: A) => Key - ) => Array]> ->(2, ( - as: ReadonlyArray, - f: (a: A) => Key -): Array]> => { - const r = new Map>() - for (const a of as) { - const k = f(a) - if (r.has(k)) { - r.get(k)!.push(a) - } else { - r.set(k, [a]) - } - } - return [...r.entries()] -}) - -export const groupByTNonEmpty = dual< - ( - f: (a: NoInfer) => Key - ) => (as: NonEmptyReadonlyArray) => NonEmptyArray]>, - ( - as: NonEmptyReadonlyArray, - f: (a: A) => Key - ) => NonEmptyArray]> ->(2, ( - as: NonEmptyReadonlyArray, - f: (a: A) => Key -): NonEmptyArray]> => { - const r = new Map>() - for (const a of as) { - const k = f(a) - if (r.has(k)) { - r.get(k)!.push(a) - } else { - r.set(k, [a]) - } - } - return [...r.entries()] as unknown as NonEmptyArray]> -}) - -// const a = [1, 2, 3] as const -// const b = [1, 2, 3] - -// const a1 = groupByTNonEmpty(a, (x) => x % 2) // $ExpectType NonEmptyReadonlyArray]> -// const b1 = groupByT(b, (x) => x % 2) // $ExpectType Array]> - -export function randomElement(a: NonEmptyReadonlyArray): A -export function randomElement(a: ReadonlyArray): A | undefined -export function randomElement(a: ReadonlyArray): A | undefined { - return a[Math.floor(Math.random() * a.length)] -} - -export function filterWith(self: ReadonlyArray, predicates: ReadonlyArray>) { - return self.filter((_) => predicates.every((f) => f(_))) -} - -/** - * Split the `items` array into multiple, smaller chunks of the given `size`. - */ -export function* _chunk_(items_: Iterable, size: number) { - const items = [...items_] - - while (items.length) { - yield items.splice(0, size) - } -} - -/** - * Split the `items` array into multiple, smaller chunks of the given `size`. - */ -export function chunk_(items_: Iterable, size: number) { - return Chunk.fromIterable(_chunk_(items_, size)) -} - -export function forEachEffectNA(as: NonEmptyReadonlyArray, f: (a: A) => Effect.Effect) { - return Effect.map(Effect.forEach(as, f), (_) => Option.getOrNull(toNonEmptyArray(_))) -} - -export * from "effect/Array" diff --git a/packages/effect-app/src/Chunk.ts b/packages/effect-app/src/Chunk.ts deleted file mode 100644 index 1ac48650d..000000000 --- a/packages/effect-app/src/Chunk.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { pipe, type Predicate, type Refinement } from "./Function.ts" -import * as Option from "./Option.ts" - -import * as Chunk from "effect/Chunk" -import { type Equivalence } from "effect/Equivalence" -import * as Array from "./Array.ts" - -export * from "effect/Chunk" - -export const fromIterable = Chunk.fromIterable - -export function groupByTChunk_(c: Chunk.Chunk, f: (a: A) => Key) { - return pipe(Chunk.toReadonlyArray(c), Array.groupByT(f), Chunk.fromIterable) -} - -/** - * Returns the first element that satisfies the predicate. - */ -export function findFirstMap( - f: (a: A) => Option.Option -) { - return (as: Chunk.Chunk) => { - const ass = Chunk.toReadonlyArray(as) - const len = ass.length - for (let i = 0; i < len; i++) { - const v = f(ass[i]!) - if (Option.isSome(v)) { - return v - } - } - return Option.none() - } -} - -export function toArray(c: Chunk.Chunk): T[] { - return Chunk.toReadonlyArray(c) as T[] -} - -/** - * Remove duplicates from an array, keeping the first occurrence of an element. - */ -export function uniq(E: Equivalence) { - return (self: Chunk.Chunk): Chunk.Chunk => { - let out = Chunk.fromIterable([] as A[]) - for (let i = 0; i < self.length; i++) { - const a = Chunk.getUnsafe(self, i) - if (!elem(E, a)(out)) { - out = Chunk.append(out, a) - } - } - return self.length === out.length ? self : out - } -} - -/** - * Test if a value is a member of an array. Takes a `Equivalence` as a single - * argument which returns the function to use to search for a value of type `A` - * in an array of type `Chunk`. - */ -export function elem(E: Equivalence, value: A) { - return (self: Chunk.Chunk): boolean => { - for (let i = 0; i < self.length; i++) { - if (E(Chunk.getUnsafe(self, i), value)) { - return true - } - } - return false - } -} - -export const ChunkPartition = Chunk.partition - -export const findFirstSimple: { - (self: Chunk.Chunk, refinement: Refinement): Option.Option - (self: Chunk.Chunk, predicate: Predicate): Option.Option -} = Chunk.findFirst - -export const findLastSimple: { - (self: Chunk.Chunk, refinement: Refinement): Option.Option - (self: Chunk.Chunk, predicate: Predicate): Option.Option -} = Chunk.findLast diff --git a/packages/effect-app/src/Config.ts b/packages/effect-app/src/Config.ts deleted file mode 100644 index 1dc6d489e..000000000 --- a/packages/effect-app/src/Config.ts +++ /dev/null @@ -1,7 +0,0 @@ -// `Config.nested` previously needed a local override because effect v3's `nested` -// did not run the provider's `mapInput`/`constantCase` over the nested namespace -// segment (see commit "fix Config.nested mapping"). As of effect 4.0.0-beta.84 the -// upstream `nested` threads the prefix through the lookup path, so the provider -// transform covers it — the override is obsolete and the old internals it used are -// gone. Re-export the native module directly. -export * from "effect/Config" diff --git a/packages/effect-app/src/Config/SecretURL.ts b/packages/effect-app/src/Config/SecretURL.ts deleted file mode 100644 index 684db9396..000000000 --- a/packages/effect-app/src/Config/SecretURL.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @since 1.0.0 - */ -import type * as Chunk from "effect/Chunk" -import * as Config from "effect/Config" -import type * as Equal from "effect/Equal" -import type * as Redacted from "effect/Redacted" -import * as internal from "./internal/configSecretURL.ts" - -// /** -// * @since 1.0.0 -// * @category symbols -// */ -// export const SecretURLTypeId: unique symbol = internal.SecretURLTypeId - -// /** -// * @since 1.0.0 -// * @category symbols -// */ -// export type SecretURLTypeId = typeof SecretURLTypeId - -/** - * @since 1.0.0 - * @category models - */ -// eslint-disable-next-line import/namespace -export interface SecretURL extends Redacted.Redacted, Equal.Equal { - /** @internal */ - readonly raw: Array -} - -export interface SecretURLOps {} - -export const SecretURL: SecretURLOps = {} - -/** - * @since 1.0.0 - */ -export declare namespace SecretURL {} - -/** - * @since 1.0.0 - * @category refinements - * @deprecated - */ -export const isSecretURL: (u: unknown) => u is SecretURL = internal.isSecretURL - -/** - * @since 1.0.0 - * @category constructors - */ -export const make: (bytes: Array) => SecretURL = internal.make - -/** - * @since 1.0.0 - * @category constructors - */ -export const fromChunk: (chunk: Chunk.Chunk) => SecretURL = internal.fromChunk - -/** - * @since 1.0.0 - * @category constructors - */ -export const fromString: (text: string) => SecretURL = internal.fromString - -/** - * @since 1.0.0 - * @category getters - */ -export const value: (self: SecretURL) => string = internal.value - -/** - * @since 1.0.0 - * @category unsafe - */ -export const unsafeWipe: (self: SecretURL) => void = internal.unsafeWipe - -export const secretURL = (name?: string): Config.Config => { - return Config.map(Config.NonEmptyString(name), fromString) -} diff --git a/packages/effect-app/src/Config/internal/configSecretURL.ts b/packages/effect-app/src/Config/internal/configSecretURL.ts deleted file mode 100644 index 36a3aa5b7..000000000 --- a/packages/effect-app/src/Config/internal/configSecretURL.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as Chunk from "effect/Chunk" -import * as Redacted from "effect/Redacted" -import type * as SecretURL from "../SecretURL.ts" - -/** @internal */ -export const isSecretURL = (u: unknown): u is SecretURL.SecretURL => { - return Redacted.isRedacted(u) && typeof (u as any).raw !== "undefined" -} - -/** @internal */ -export const make = (bytes: Array): SecretURL.SecretURL => { - const secret = Object.assign( - Redacted.make(bytes.map((byte) => String.fromCharCode(byte)).join("")), - { raw: undefined as any } as const - ) - let protocol = "unknown" - try { - const url = new URL(bytes.map((byte) => String.fromCharCode(byte)).join("")) - protocol = url.protocol.substring(0, url.protocol.length - 1) - } catch { - const matches = protocol.match(/^([^:]+):\/\//) - if (matches !== null) { - protocol = matches[1]! - } - } - Object.defineProperty(secret, "toJSON", { - enumerable: false, - value() { - return ({ - _tag: "SecretURL", - protocol - }) - } - }) - Object.defineProperty(secret, "toString", { - enumerable: false, - value() { - return `SecretURL(${protocol}://)` - } - }) - Object.defineProperty(secret, "raw", { - enumerable: false, - value: bytes - }) - return secret -} - -/** @internal */ -export const fromChunk = (chunk: Chunk.Chunk): SecretURL.SecretURL => { - return make(Chunk.toReadonlyArray(chunk).map((char) => char.charCodeAt(0))) -} - -/** @internal */ -export const fromString = (text: string): SecretURL.SecretURL => { - return make(text.split("").map((char) => char.charCodeAt(0))) -} - -/** @internal */ -export const value = (self: SecretURL.SecretURL): string => { - return self.raw.map((byte) => String.fromCharCode(byte)).join("") -} - -/** @internal */ -export const unsafeWipe = (self: SecretURL.SecretURL): void => { - for (let i = 0; i < self.raw.length; i++) { - self.raw[i] = 0 - } -} diff --git a/packages/effect-app/src/ConfigProvider.ts b/packages/effect-app/src/ConfigProvider.ts deleted file mode 100644 index 881a627a2..000000000 --- a/packages/effect-app/src/ConfigProvider.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Local `nested` override removed: it existed to make the nested namespace honour the -// provider's `mapInput`/`constantCase` (see commit "fix Config.nested mapping"), which -// effect 4.0.0-beta.84 now does natively. The old override also relied on internals -// (`provider.get`/`mapInput`/`prefix`) that no longer exist. Re-export native directly. -export * from "effect/ConfigProvider" diff --git a/packages/effect-app/src/Context.ts b/packages/effect-app/src/Context.ts deleted file mode 100644 index 8846a8fa8..000000000 --- a/packages/effect-app/src/Context.ts +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable import/namespace */ - -import * as CTX from "effect/Context" -import type * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import type * as Scope from "effect/Scope" -import type * as Types from "effect/Types" - -export * from "effect/Context" - -export interface Opaque extends CTX.Key { - of(this: void, self: Shape): Self - context(self: Shape): CTX.Context - // a version that leverages the Shape -> Self conversion - toLayer: ( - eff: Effect.Effect - ) => Layer.Layer> - use(f: (service: Shape) => Effect.Effect): Effect.Effect - useSync(f: (service: Shape) => A): Effect.Effect -} - -// export interface OpaqueMake -// extends CTX.Service -// { -// // temp while sorting out https://github.com/Effect-TS/effect-smol/pull/1534 -// of(self: Shape): Self -// contextMap2(self: Shape): CTX.Context -// // a version that leverages the Shape -> Self conversion -// toLayer: { -// ( -// eff: Effect.Effect -// ): Layer.Layer> -// (): Layer.Layer> -// } -// } - -export function assignTag( - key: string, - creationError?: Error -) { - return (cls: S): S & Opaque => { - const tag = CTX.Service(key) - let fields = tag - if (Reflect.ownKeys(cls).includes("key")) { - const { key, ...rest } = tag - fields = rest as any - } - const t = Object.assign(cls, Object.getPrototypeOf(tag), fields) - if (!creationError) { - const limit = Error.stackTraceLimit - Error.stackTraceLimit = 2 - creationError = new Error() - Error.stackTraceLimit = limit - } - // the stack is used to get the location of the tag definition, if a service is not found in the registry - Object.defineProperty(t, "stack", { - get() { - return creationError!.stack - } - }) - return t - } -} - -/** Accessor for a service method that returns a plain value. Wraps via `useSync`. */ -export const accessFn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect.Effect - : never => ((...args: Array) => Tag.useSync((s: any) => s[key](...args))) as any - -/** Accessor for a service method that returns an Effect. Delegates via `use`. */ -export const accessEffectFn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends (...args: [...infer Args]) => Effect.Effect - ? (...args: Readonly) => Effect.Effect - : never => ((...args: Array) => Tag.use((s: any) => s[key](...args))) as any - -/** Accessor for a service property (constant). Wraps via `useSync`. */ -export const accessCn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Effect.Effect => Tag.useSync((s) => s[key]) - -/** Accessor for a service property that is an Effect. Delegates via `use`. */ -export const accessEffectCn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends Effect.Effect ? Effect.Effect - : never => Tag.use((s: any) => s[key]) as any - -export const TypeId = "~Context.Opaque" - -// export function Opaque(key: Key) { -// return () => { -// const limit = Error.stackTraceLimit -// Error.stackTraceLimit = 2 -// const creationError = new Error() -// Error.stackTraceLimit = limit -// const c: abstract new(_: never) => Shape & { readonly [TypeId]: Key } = class {} as any - -// return assignTag(key, creationError)(c) -// } -// } - -export interface OpaqueClass - extends Opaque -{ - new(_: never): Shape & { readonly [TypeId]: Identifier } - readonly key: Identifier -} - -// export interface OpaqueClassMake -// extends OpaqueMake -// { -// new(_: never): Shape & { readonly [TypeId]: Identifier } -// readonly key: Identifier -// } - -export const Opaque: { - (): < - const Identifier extends string, - E, - R = Types.unassigned, - Args extends ReadonlyArray = never - >( - id: Identifier, - options?: { - readonly make: ((...args: Args) => Effect.Effect) | Effect.Effect | undefined - } - ) => - & OpaqueClass - & ([Types.unassigned] extends [R] ? unknown - : { - readonly make: [Args] extends [never] ? Effect.Effect - : (...args: Args) => Effect.Effect - }) - (): < - const Identifier extends string, - Make extends Effect.Effect | ((...args: any) => Effect.Effect) - >( - id: Identifier, - options: { - readonly make: Make - } - ) => - & OpaqueClass< - Self, - Identifier, - Make extends - | Effect.Effect - | ((...args: infer _Args) => Effect.Effect) ? _A - : never - > - & { readonly make: Make } -} = () => (id: string, options: any) => { - const svc = CTX.Service()(id, options) as any - return Object.assign(svc, { - toLayer: (eff: Effect.Effect) => { - return Layer.effect(svc, eff) - } - }) -} diff --git a/packages/effect-app/src/DataDependencies.ts b/packages/effect-app/src/DataDependencies.ts deleted file mode 100644 index 920fc7cb2..000000000 --- a/packages/effect-app/src/DataDependencies.ts +++ /dev/null @@ -1,171 +0,0 @@ -import type { NonEmptyReadonlyArray } from "effect/Array" -import * as Ref from "effect/Ref" -import * as Stream from "effect/Stream" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import * as S from "./Schema.ts" - -export const DataDependency = S.Union([ - S.Struct({ - type: S.Literal("repo"), - name: S.String, - ids: S.optional(S.NonEmptyArray(S.String)) - }), - S.Struct({ - type: S.Literal("signal"), - name: S.String, - ids: S.optional(S.NonEmptyArray(S.String)) - }) -]) -export type DataDependency = S.Schema.Type - -export const DataDependencies = S.ReadonlySet(DataDependency) -export type DataDependencies = S.Schema.Type - -export const empty = (): DataDependencies => new Set() - -export const DataDependencySet = S.Struct({ - reads: DataDependencies, - writes: DataDependencies -}) -export type DataDependencySet = S.Schema.Type - -export interface DataDependencyRecorderService { - readonly read: (dependency: DataDependency) => Effect.Effect - readonly write: (dependency: DataDependency) => Effect.Effect - readonly get: Effect.Effect - readonly drain: Effect.Effect - readonly drainWrites: Effect.Effect -} - -const sameDomain = (a: DataDependency, b: DataDependency) => a.type === b.type && a.name === b.name - -const dependenciesOverlap = (a: DataDependency, b: DataDependency) => { - if (!sameDomain(a, b)) return false - if (a.ids === undefined || b.ids === undefined) return true - return a.ids.some((id) => b.ids?.includes(id)) -} - -const containsDependency = (dependencies: ReadonlySet, dependency: DataDependency) => { - for (const _ of dependencies) { - if (dependenciesOverlap(_, dependency)) return true - } - return false -} - -const appendDependency = (dependency: DataDependency) => (dependencies: DataDependencies): DataDependencies => { - const existing = [...dependencies].find((_) => sameDomain(_, dependency)) - if (existing === undefined) return new Set([...dependencies, dependency]) - if (existing.ids === undefined) return dependencies - if (dependency.ids === undefined) { - return new Set([...dependencies].filter((_) => _ !== existing).concat(dependency)) - } - const first = existing.ids[0] - const ids: NonEmptyReadonlyArray = [ - first, - ...new Set([...existing.ids.slice(1), ...dependency.ids].filter((id) => id !== first)) - ] - if (ids.length === existing.ids.length) return dependencies - const merged = existing.type === "repo" ? repo(existing.name, ids) : signal(existing.name, ids) - return new Set([...dependencies].filter((_) => _ !== existing).concat(merged)) -} - -export const merge = (...groups: ReadonlyArray): DataDependencies => { - let merged = empty() - for (const group of groups) { - for (const dependency of group) merged = appendDependency(dependency)(merged) - } - return merged -} - -export const DataDependencyRecorder = RequestScopedDependencies.make( - "effect-app/DataDependencyRecorder", - Effect.gen(function*() { - const readsRef = yield* Ref.make(empty()) - const writesRef = yield* Ref.make(empty()) - return makeDataDependencyRecorder(readsRef, writesRef) - }) -) -export type DataDependencyRecorder = typeof DataDependencyRecorder - -export type DataDependencyRecorderNotStartedError = RequestScopedDependencies.RequestScopedDependencyNotStartedError - -export const getDataDependencyRecorder = DataDependencyRecorder.current - -/** - * Guarantee a recorder is in scope for `self`, providing a fresh one per request when none is - * (e.g. a top-level client call with no parent query/command tracking its dependencies). An - * existing recorder is inherited, so nested calls forward into their parent rather than shadow it. - */ -export const ensureDataDependencyRecorder = (self: Effect.Effect): Effect.Effect => - Effect.flatMap( - DataDependencyRecorder, - (current) => current === "root" ? Effect.provide(self, DataDependencyRecorder.layer) : self - ) - -/** Stream variant of {@link ensureDataDependencyRecorder}. */ -export const ensureDataDependencyRecorderStream = (self: Stream.Stream): Stream.Stream => - Stream.unwrap( - Effect.map( - DataDependencyRecorder, - (current) => current === "root" ? Stream.provide(self, DataDependencyRecorder.layer) : self - ) - ) - -export const makeDataDependencyRecorder = ( - readsRef: Ref.Ref, - writesRef: Ref.Ref -): DataDependencyRecorderService => ({ - read: (dependency) => Ref.update(readsRef, appendDependency(dependency)), - write: (dependency) => Ref.update(writesRef, appendDependency(dependency)), - get: Effect.all({ - reads: Ref.get(readsRef), - writes: Ref.get(writesRef) - }), - drain: Effect.all({ - reads: Ref.getAndSet(readsRef, empty()), - writes: Ref.getAndSet(writesRef, empty()) - }), - drainWrites: Ref.getAndSet(writesRef, empty()) -}) - -export const repo = (name: string, ids?: NonEmptyReadonlyArray): DataDependency => - ids === undefined ? { type: "repo", name } : { type: "repo", name, ids } -export const signal = (name: string, ids?: NonEmptyReadonlyArray): DataDependency => - ids === undefined ? { type: "signal", name } : { type: "signal", name, ids } - -const RepoReadScope = Context.Reference("effect-app/DataDependencies/RepoReadScope", { - defaultValue: () => undefined -}) - -export const readRepo = (name: string) => - Effect.flatMap( - RepoReadScope, - (scope) => read(scope?.type === "repo" && scope.name === name ? scope : repo(name)) - ) - -export const withRepoReadScope = - (name: string, ids: NonEmptyReadonlyArray) => - (self: Effect.Effect): Effect.Effect => - Effect.provideService(self, RepoReadScope, repo(name, ids)) - -export const QueryReadDependenciesMetaKey = "effect-app.query.readDependencies" - -export const read = (dependency: DataDependency) => - getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.read(dependency))) - -export const write = (dependency: DataDependency) => - getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.write(dependency))) - -export const intersects = ( - a: ReadonlySet, - b: ReadonlySet -) => { - for (const dependency of a) { - if (containsDependency(b, dependency)) return true - } - return false -} - -export const isNonEmpty = (dependencies: DataDependencies) => dependencies.size > 0 diff --git a/packages/effect-app/src/Effect.ts b/packages/effect-app/src/Effect.ts deleted file mode 100644 index 0685cdbeb..000000000 --- a/packages/effect-app/src/Effect.ts +++ /dev/null @@ -1,202 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable prefer-destructuring */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars - -import * as Def from "effect/Deferred" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Ref from "effect/Ref" -import type { Scope } from "effect/Scope" -import type { Semaphore } from "effect/Semaphore" -import type * as Context from "./Context.ts" -import { curry } from "./Function.ts" -import { typedKeysOf } from "./utils.ts" - -export * from "effect/Effect" -// v4: Effect interface not re-exported by `export *` due to local binding collision -export type { Effect } from "effect/Effect" - -export function flatMapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => Effect.Effect -): Effect.Effect, E | E2, R | R2> { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm(_), Option.some) - })) -} - -export function tapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => Effect.Effect -): Effect.Effect, E | E2, R | R2> { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm(_), () => Option.some(_)) - })) -} - -export function zipRightOption( - self: Effect.Effect, E, R>, - fm: Effect.Effect -) { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm, () => Option.some(_)) - })) -} - -export function mapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => A2 -): Effect.Effect, E, R> { - return Effect.map(self, (d) => - Option.match(d, { - onNone: () => Option.none(), - onSome: (_) => Option.some(fm(_)) - })) -} - -/** - * Takes [A, B], applies it to a curried Effect function, - * taps the Effect, returning A. - */ -export function tupleTap( - f: (b: B) => (a: A) => Effect.Effect -) { - return (t: readonly [A, B]) => Effect.sync(() => t[0]).pipe(Effect.tap(f(t[1]))) -} - -/** - * Takes [A, B], applies it to an Effect function, - * taps the Effect, returning A. - */ -export function tupleTap_(f: (a: A, b: B) => Effect.Effect) { - return tupleTap(curry(f)) -} - -export function ifDiffR(f: (i: I) => Effect.Effect) { - return (n: I, orig: I) => ifDiff_(n, orig, f) -} - -export function ifDiff_( - n: I, - orig: I, - f: (i: I) => Effect.Effect -) { - return n !== orig ? f(n) : Effect.void -} - -export function ifDiff(n: I, orig: I) { - return (f: (i: I) => Effect.Effect) => ifDiff_(n, orig, f) -} - -// NOTE: await extension doesnt work via tsplus somehow -export const await_ = Def.await - -/** - * Ref has atomic modify support if synchronous, for Effect we need a Semaphore. - */ -export function modifyWithPermitWithEffect(ref: Ref.Ref, semaphore: Semaphore) { - const withPermit = semaphore.withPermits(1) - return (mod: (a: A) => Effect.Effect) => - withPermit( - Effect - .flatMap(Ref.get(ref), mod) - .pipe( - Effect.tap(([, _]) => Ref.set(ref, _)), - Effect.map(([_]) => _) - ) - ) -} - -type ServiceA = T extends Effect.Effect ? S - : T extends Context.Service ? S - : never -type ServiceR = T extends Effect.Effect ? R - : T extends Context.Service ? I - : never -type ServiceE = T extends Effect.Effect ? E : never -// type Values = T extends { [s: string]: infer S } ? ServiceA : never -type ValuesR = T extends { [s: string]: infer S } ? ServiceR : never -type ValuesE = T extends { [s: string]: infer S } ? ServiceE : never - -/** - * Due to tsplus unification (tsplus unify tag), when trying to use the Effect type in a type constraint - * the compiler will cause basically anything to match. as such, use this type instead. - * ```ts - * const a = < - * SVC extends Record< - * string, - * ((req: number) => Effect.Effect) | Effect.Effect - * > - * >(svc: SVC) => svc - * - * const b = a({ str: "" }) // valid, but shouldn't be! - * ``` - */ -// eslint-disable-next-line import/namespace -export interface EffectUnunified extends Effect.Effect {} - -export type LowerFirst = S extends `${infer First}${infer Rest}` ? `${Lowercase}${Rest}` - : S -export type LowerServices | Effect.Effect>> = { - [key in keyof T as LowerFirst]: ServiceA -} - -export function allLower | Effect.Effect>>( - services: T -) { - return Effect.all( - typedKeysOf(services).reduce((prev, cur) => { - const svc = services[cur]! - prev[((cur as string)[0]!.toLowerCase() + (cur as string).slice(1)) as unknown as LowerFirst] = svc - return prev - }, {} as any), - { concurrency: "unbounded" } - ) as any as Effect.Effect, ValuesE, ValuesR> -} - -export function allLowerWith | Effect.Effect>, A>( - services: T, - fn: (services: LowerServices) => A -) { - return Effect.map(allLower(services), fn) -} - -export function allLowerWithEffect< - T extends Record | Effect.Effect>, - R, - E, - A ->( - services: T, - fn: (services: LowerServices) => Effect.Effect -) { - return Effect.flatMap(allLower(services), fn) -} - -/** - * Recovers from all errors. - */ -export function catchAllMap(f: (e: E) => A2) { - return (self: Effect.Effect): Effect.Effect => - Effect.catch(self, (err: E) => Effect.sync(() => f(err))) -} - -/** - * Annotates each log in this scope with the specified log annotation. - */ -export function annotateLogscoped(key: string, value: string): Effect.Effect { - return Effect.annotateLogsScoped(key, value) -} - -/** - * Annotates each log in this scope with the specified log annotations. - */ -export function annotateLogsScoped(kvps: Record): Effect.Effect { - return Effect.annotateLogsScoped(kvps) -} diff --git a/packages/effect-app/src/Emailer.ts b/packages/effect-app/src/Emailer.ts deleted file mode 100644 index 1fe5eab07..000000000 --- a/packages/effect-app/src/Emailer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as Data from "effect/Data" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import * as Context from "./Context.ts" -import type * as Effect from "./Effect.ts" -import type { Email } from "./Schema.ts" - -export class SendMailError extends Data.TaggedError("SendMailError")<{ - readonly raw: Error -}> {} - -export class Emailer extends Context.Opaque Effect.Effect -}>()("effect-app/Emailer") {} - -export type EmailData = Email | { - name?: string - email: Email -} - -export interface EmailContentPart { - type: string - value: string -} - -export type EmailRecipients = EmailData | NonEmptyReadonlyArray - -export interface EmailMsgBase { - readonly to: EmailRecipients - readonly cc?: EmailRecipients - readonly bcc?: EmailRecipients - readonly from: EmailData - readonly replyTo?: EmailData - readonly subject?: string - /** - * should multiple `to` addresess be considered multiple emails? - * defaults to `true`, not to leak email addresses - */ - readonly isMultiple?: boolean -} - -export type EmailContent = - | { text: string } - | { html: string } - | { templateId: string } - | { content: NonEmptyReadonlyArray } - -export type EmailMsg = - & EmailMsgBase - & EmailContent - -export type EmailMsgOptionalFrom = Omit & Partial> & EmailContent diff --git a/packages/effect-app/src/Function.ts b/packages/effect-app/src/Function.ts deleted file mode 100644 index 1f56364fe..000000000 --- a/packages/effect-app/src/Function.ts +++ /dev/null @@ -1,175 +0,0 @@ -export * from "effect/Function" -export type { Predicate, Refinement } from "effect/Predicate" - -/** - * Construct tuples - */ -export function tuple>(...t: T): Readonly { - return t -} - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -export function tupledCurry(f: (b: B) => (a: A) => C) { - return (t: [A, B]) => f(t[1])(t[0]) -} - -export function reverseCurry(f: (b: B) => (a: A) => C) { - return (a: A) => (b: B) => f(b)(a) -} - -export function curry(f: (a: A, b: B) => C) { - return (b: B) => (a: A) => f(a, b) -} - -export function uncurry(f: (b: B) => (a: A) => C) { - return (a: A, b: B) => f(b)(a) -} - -// It's better to have these expanded, so that on the call site you understand the functions better. -// type OptMagix = ((b?: B) => (a: A) => C) & { -// /** -// * Uncurried version of the parent function -// */ -// _: (a: A, b?: B) => C -// /** -// * Reverse curried version of the parent function -// */ -// r: (a: A) => (b?: B) => C -// } -// type Magix = ((b: B) => (a: A) => C) & { -// /** -// * Uncurried version of the parent function -// */ -// _: (a: A, b: B) => C -// /** -// * Reverse curried version of the parent function -// */ -// r: (a: A) => (b: B) => C -// } - -export function curriedMagix( - f: (b?: B) => (a: A) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function curriedMagix( - f: (b: B) => (a: A) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function curriedMagix(f: (b: B) => (a: A) => C) { - return Object.assign(f, { - /** - * Uncurried version of the parent function - */ - _: uncurry(f), - /** - * Reverse curried version of the parent function - */ - r: reverseCurry(f) - }) -} - -export function uncurriedMagix( - f: (a: A, b?: B) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function uncurriedMagix( - f: (a: A, b: B) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function uncurriedMagix(f: (a: A, b: B) => C) { - const curried = curry(f) - return Object.assign(curried, { - /** - * Uncurried version of the parent function - */ - _: f, - /** - * Reverse curried version of the parent function - */ - r: reverseCurry(curried) - }) -} - -export function reverseCurriedMagix( - f: (a: A) => (b: B) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function reverseCurriedMagix( - f: (a?: A) => (b: B) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function reverseCurriedMagix(f: (a: A) => (b: B) => C) { - const curried = reverseCurry(f) - return Object.assign(curried, { - /** - * Uncurried version of the parent function - */ - _: uncurry(curried), - /** - * Reverse curried version of the parent function - */ - r: f - }) -} - -export function lazy(f: () => A): () => A { - let tmp: A | undefined - let done = false - return () => { - if (done) { - return tmp! - } - tmp = f() - done = true - return tmp - } -} diff --git a/packages/effect-app/src/Inputify.type.ts b/packages/effect-app/src/Inputify.type.ts deleted file mode 100644 index a47022685..000000000 --- a/packages/effect-app/src/Inputify.type.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { BuiltInObject } from "effect-app/utils" - -import type { Primitive } from "./Widen.type.ts" - -// Get rid of Date | string, and replace with Date | null -export type Inputify = Date extends T ? string extends T ? Date | null - : Date | null - : [T] extends [BuiltInObject] | [Primitive] ? T - : [T] extends [Array] ? { [K in keyof T]: Inputify } - : [T] extends [ReadonlyArray] ? { [K in keyof T]: Inputify } - : [T] extends [object] ? { [K in keyof T]: Inputify } - : T diff --git a/packages/effect-app/src/Layer.ts b/packages/effect-app/src/Layer.ts deleted file mode 100644 index 6f1c820b2..000000000 --- a/packages/effect-app/src/Layer.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type * as Array from "effect/Array" -import * as Effect from "effect/Effect" -import { dual } from "effect/Function" -import * as Layer from "effect/Layer" -import type * as Scope from "effect/Scope" -import type * as Types from "effect/Types" -import type * as Context from "./Context.ts" -import { type EffectGenUtils } from "./utils/gen.ts" - -export * from "effect/Layer" - -type Make = DependenciesOpt & (MakeEff | MakeGenNo | MakeGen) - -type MakeEff = { - readonly make: Effect.Effect -} -type MakeGen = { - readonly make: () => Generator, S> -} -type MakeGenNo = { - readonly make: () => Generator -} -type MakeErr = Opts extends { make: () => any } ? EffectGenUtils.Error : never -type MakeContext = Opts extends { make: () => any } ? EffectGenUtils.Context : never - -type DependenciesOpt = { dependencies?: Array.NonEmptyReadonlyArray } -type Dependencies = { dependencies: Array.NonEmptyReadonlyArray } - -// Local replacements for removed Effect.Service.MakeDeps* types -type MakeDepsE = Opts extends { dependencies: ReadonlyArray> } ? E : never -type MakeDepsOut = Opts extends { dependencies: ReadonlyArray> } ? Out : never - -type PackedLayers = - & Layer.Layer< - I, - MakeErr | MakeDepsE, - Exclude, Scope.Scope | MakeDepsOut> - > - & { - withoutDependencies: Layer.Layer, Exclude, Scope.Scope>> - } - -type PackedOrUnpackedLayer = Opts extends Dependencies ? PackedLayers & {} - : Layer.Layer, MakeContext> - -export const make: { - ( - tag: Context.Service - ): , any, any>>( - options: Opts - ) => PackedOrUnpackedLayer - , any, any>>( - tag: Context.Service, - options: Opts - ): PackedOrUnpackedLayer -} = dual(2, (tag, options) => { - const effect = options.make[Symbol.toStringTag] === "GeneratorFunction" - ? Effect.fnUntraced(options.make)() - : options.make - const withoutDependencies = Layer.effect(tag, effect) - if (options.dependencies) { - return Object.assign( - withoutDependencies.pipe(Layer.provide(options.dependencies)), - { withoutDependencies } - ) - } else { - return withoutDependencies - } -}) diff --git a/packages/effect-app/src/Model.ts b/packages/effect-app/src/Model.ts deleted file mode 100644 index fdfa6b568..000000000 --- a/packages/effect-app/src/Model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./Model/dsl.ts" -export * as Q from "./Model/query.ts" -export { makeRepo } from "./Model/Repository.ts" -export { repositoryDependency } from "./Model/Repository.ts" -export { type RegisteredRepository, RepositoryRegistry, RepositoryRegistryLive } from "./Model/Repository.ts" diff --git a/packages/effect-app/src/Model/Repository.ts b/packages/effect-app/src/Model/Repository.ts deleted file mode 100644 index f68b0b3e0..000000000 --- a/packages/effect-app/src/Model/Repository.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { repositoryDependency } from "./Repository/dependency.ts" -export * from "./Repository/ext.ts" -export * from "./Repository/legacy.ts" -export { makeRepo } from "./Repository/makeRepo.ts" -export * from "./Repository/Registry.ts" -export * from "./Repository/service.ts" -export * from "./Repository/validation.ts" diff --git a/packages/effect-app/src/Model/Repository/Registry.ts b/packages/effect-app/src/Model/Repository/Registry.ts deleted file mode 100644 index 6fb1a675b..000000000 --- a/packages/effect-app/src/Model/Repository/Registry.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { DatabaseError } from "../../client/errors.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" - -export interface RegisteredRepository { - readonly seedNamespace: (namespace: string) => Effect.Effect -} - -const make = Effect.sync(() => { - const repos = new Map() - return { - register(modelName: string, repo: RegisteredRepository) { - repos.set(modelName, repo) - }, - seedNamespace: (namespace: string) => - Effect.suspend(() => - Effect.forEach( - repos.values(), - (r) => r.seedNamespace(namespace), - { concurrency: "unbounded", discard: true } - ) - ), - get entries(): ReadonlyMap { - return repos - } - } -}) - -export class RepositoryRegistry extends Context.Opaque void - readonly seedNamespace: (namespace: string) => Effect.Effect - readonly entries: ReadonlyMap -}>()("effect-app/RepositoryRegistry", { make }) {} - -export const RepositoryRegistryLive = RepositoryRegistry.toLayer(RepositoryRegistry.make) diff --git a/packages/effect-app/src/Model/Repository/dependency.ts b/packages/effect-app/src/Model/Repository/dependency.ts deleted file mode 100644 index adc7e64d9..000000000 --- a/packages/effect-app/src/Model/Repository/dependency.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as S from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" - -const RepositoryDependencyAnnotation = "effect-app/Repository/dependency" - -/** Marks an encoded model field as an identity alias for repository invalidation. */ -export const repositoryDependency = (schema: Schema): Schema["Rebuild"] => - S.annotateEncoded({ [RepositoryDependencyAnnotation]: true })(schema) - -/** @internal */ -export const repositoryDependencyPaths = (schema: S.Schema): readonly string[] => { - const visit = (ast: SchemaAST.AST, path: readonly string[]): readonly string[] => { - const annotations = ast.checks?.at(-1)?.annotations ?? ast.annotations - if (annotations?.[RepositoryDependencyAnnotation] === true) return [path.join(".")] - if (SchemaAST.isDeclaration(ast)) return ast.typeParameters.flatMap((parameter) => visit(parameter, path)) - if (SchemaAST.isUnion(ast)) return ast.types.flatMap((member) => visit(member, path)) - if (SchemaAST.isObjects(ast)) { - return ast.propertySignatures.flatMap((property) => - typeof property.name === "string" ? visit(property.type, [...path, property.name]) : [] - ) - } - if (SchemaAST.isArrays(ast)) { - return [ - ...ast.elements.flatMap((element, index) => visit(element, [...path, String(index)])), - ...ast.rest.flatMap((element) => visit(element, [...path, "-1"])) - ] - } - return [] - } - - return [...new Set(visit(SchemaAST.toEncoded(schema.ast), []))] -} diff --git a/packages/effect-app/src/Model/Repository/ext.ts b/packages/effect-app/src/Model/Repository/ext.ts deleted file mode 100644 index d8fedfbef..000000000 --- a/packages/effect-app/src/Model/Repository/ext.ts +++ /dev/null @@ -1,374 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -import * as Exit from "effect/Exit" -import * as Request from "effect/Request" -import * as RequestResolver from "effect/RequestResolver" -import * as Array from "../../Array.ts" -import type { NonEmptyArray } from "../../Array.ts" -import { type DatabaseError, type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../../client/errors.ts" -import * as Effect from "../../Effect.ts" -import * as Option from "../../Option.ts" -import { type FixEnv, type PureEnv, runTerm } from "../../Pure.ts" -import { AnyPureDSL } from "../dsl.ts" -import type { FieldValues } from "../filter/types.ts" -import type { Query, QueryEnd, QueryWhere } from "../query.ts" -import * as Q from "../query.ts" -import type { Repository } from "./service.ts" - -interface BatchOptions { - readonly batch?: true | number -} - -const asReadonlyArray = (itemOrItems: T | ReadonlyArray): ReadonlyArray => - globalThis.Array.isArray(itemOrItems) - ? itemOrItems as ReadonlyArray - : [itemOrItems as T] - -const getBatchSize = (batch?: true | number) => - batch === true - ? 100 - : typeof batch === "number" && Number.isFinite(batch) && batch > 0 - ? Math.floor(batch) - : undefined - -export const extendRepo = < - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T & keyof Encoded, - RSchema, - RPublish, - RProvided = never ->( - repo: Repository -) => { - const get = (id: T[IdKey]) => - repo.find(id).pipe( - Effect.flatMap(Option.match({ - onNone: () => Effect.fail(new NotFoundError({ type: repo.itemType, id })), - onSome: Effect.succeed - })) - ) - function saveManyWithPure_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - items: Iterable, - pure: Effect.Effect> - ) { - return saveAllWithEffectInt( - runTerm(pure, [...items]) - ) - } - - function saveWithPure_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - item: S1, - pure: Effect.Effect> - ) { - return saveAllWithEffectInt( - runTerm(pure, item) - .pipe(Effect - .map(([item, events, a]) => [[item], events, a])) - ) - } - - function saveAllWithEffectInt< - P extends T, - R, - E, - A - >( - gen: Effect.Effect, Iterable, A], E, R> - ): Effect.Effect< - A, - E | InvalidStateError | OptimisticConcurrencyException | DatabaseError, - R | RSchema | RPublish - > { - return Effect.flatMap(gen, ([items, events, a]) => repo.saveAndPublish(items, events).pipe(Effect.map(() => a))) - } - - function saveManyWithPureBatched_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - items: Iterable, - pure: Effect.Effect>, - batchSize = 100 - ) { - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => - saveAllWithEffectInt( - runTerm(pure, batch) - ) - ) - } - - type PureStoreError = InvalidStateError | OptimisticConcurrencyException | DatabaseError - - const queryAndSavePure: { - ( - q: ( - q: Query - ) => QueryEnd, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | NotFoundError | E2, - | RSchema - | RPublish - | Exclude - }> - > - ( - q: ( - q: Query - ) => - | Query - | QueryWhere - | QueryEnd, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | E2, - | RSchema - | RPublish - | Exclude - }> - > - ( - q: ( - q: Query - ) => - | Query - | QueryWhere - | QueryEnd, - pure: Effect.Effect>, - batch: "batched" | number - ): Effect.Effect< - A[], - PureStoreError | E2, - | RSchema - | RPublish - | Exclude - }> - > - } = ((q: any, pure: any, batch?: "batched" | number) => - // Overload dispatch: query returns T | T[]; pure helpers are generic on item shape. - // Runtime path is query → pure term → saveAndPublish (which raises DatabaseError). - repo.query(q).pipe( - Effect.andThen((_) => - Array.isArray(_) - ? batch === undefined - ? saveManyWithPure_(_ as T[], pure) - : saveManyWithPureBatched_(_ as T[], pure, batch === "batched" ? 100 : batch) - : saveWithPure_(_ as T, pure) - ) - )) as typeof queryAndSavePure - - const saveManyWithPure: { - ( - items: Iterable, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | E, - | RSchema - | RPublish - | Exclude - }> - > - ( - items: Iterable, - pure: Effect.Effect>, - batch: "batched" | number - ): Effect.Effect< - A[], - PureStoreError | E, - | RSchema - | RPublish - | Exclude - }> - > - } = ((items: Iterable, pure: any, batch?: "batched" | number) => - batch - ? Effect.forEach( - Array.chunksOf([...items], batch === "batched" ? 100 : batch), - (batchItems) => - saveAllWithEffectInt( - runTerm(pure, batchItems as any) - ) - ) - : saveAllWithEffectInt( - runTerm(pure, [...items] as any) - )) as typeof saveManyWithPure - - const byIdAndSaveWithPure: { - ( - id: T[IdKey], - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | NotFoundError | E, - | RSchema - | RPublish - | Exclude - }> - > - } = (id, pure) => get(id).pipe(Effect.flatMap((item) => saveWithPure_(item, pure))) - - // query can raise DatabaseError; NotFound is completed per-entry. Dual package - // round-trips may still surface other store errors via failCause — keep channel honest. - type Req = - & Request.Request | DatabaseError> - & { _tag: `Get${ItemType}`; id: T[IdKey] } - const _request = Request.tagged(`Get${repo.itemType}`) - - const requestResolver = RequestResolver - .make(( - entries: NonEmptyArray>, - _key: unknown - ) => - (repo.query(Q.where(repo.idKey as any, "in" as any, entries.map((_) => _.request.id)) as any) as Effect.Effect< - readonly T[], - DatabaseError - >) - .pipe( - Effect.andThen((items) => - Effect.forEach(entries, (entry) => - Request.complete( - Array - .findFirst(items, (_) => _[repo.idKey] === entry.request.id) - .pipe(Option.match({ - onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: entry.request.id })), - onSome: Exit.succeed - })) - )(entry), { discard: true }) - ), - Effect - .catchCause((cause) => - Effect.forEach(entries, (entry) => Request.complete(Exit.failCause(cause))(entry), { discard: true }) - ) - ) - ) - .pipe( - RequestResolver.batchN(20) - ) - - const exts = { - request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver), - get, - log: (evt: Evt) => AnyPureDSL.log(evt), - /** - * Enables chunked writes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are saved independently. - */ - save: (itemOrItems: T | ReadonlyArray, options?: BatchOptions) => { - const items = asReadonlyArray(itemOrItems) - if (!Array.isReadonlyArrayNonEmpty(items)) { - return Effect.void - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.saveAndPublish(items) - } - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => repo.saveAndPublish(batch), - { discard: true } - ) - }, - saveWithEvents: (events: Iterable) => (...items: NonEmptyArray) => repo.saveAndPublish(items, events), - /** - * Enables chunked deletes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are removed independently. - */ - remove: (itemOrItems: T | ReadonlyArray, options?: BatchOptions) => { - const items = asReadonlyArray(itemOrItems) - if (!Array.isReadonlyArrayNonEmpty(items)) { - return Effect.void as Effect.Effect - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.removeAndPublish(items) - } - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => repo.removeAndPublish(batch), - { discard: true } - ) - }, - /** - * Enables chunked deletes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are removed independently. - */ - removeById: (idOrIds: T[IdKey] | ReadonlyArray, options?: BatchOptions) => { - const ids = asReadonlyArray(idOrIds) - if (!Array.isReadonlyArrayNonEmpty(ids)) { - return Effect.void as Effect.Effect - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.removeById(ids) - } - return Effect.forEach( - Array.chunksOf(ids, batchSize), - (batch) => repo.removeById(batch), - { discard: true } - ) - }, - queryAndSavePure, - saveManyWithPure, - byIdAndSaveWithPure, - saveWithPure: < - R, - A, - E, - S1 extends T, - S2 extends T - >( - item: S1, - pure: Effect.Effect> - ) => - saveAllWithEffectInt( - runTerm(pure, item) - .pipe(Effect.map(([item, events, a]) => [[item], events, a])) - ) - } - - return { - ...repo, - ...exts - } as Repository & typeof exts -} - -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface ExtendedRepository< - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T & keyof Encoded, - RSchema, - RPublish, - RProvided = never -> extends ReturnType> {} diff --git a/packages/effect-app/src/Model/Repository/internal/internal.ts b/packages/effect-app/src/Model/Repository/internal/internal.ts deleted file mode 100644 index 07bc29355..000000000 --- a/packages/effect-app/src/Model/Repository/internal/internal.ts +++ /dev/null @@ -1,988 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Equivalence from "effect/Equivalence" -import { flow, pipe } from "effect/Function" -import * as HashMap from "effect/HashMap" -import * as HashSet from "effect/HashSet" -import * as Metric from "effect/Metric" -import * as Pipeable from "effect/Pipeable" -import * as Ref from "effect/Ref" -import * as Result from "effect/Result" -import type * as Scope from "effect/Scope" -import * as Unify from "effect/Unify" -import * as Array from "../../../Array.ts" -import type { NonEmptyReadonlyArray } from "../../../Array.ts" -import { toNonEmptyArray } from "../../../Array.ts" -import * as Chunk from "../../../Chunk.ts" -import { NotFoundError } from "../../../client/errors.ts" -import * as Context from "../../../Context.ts" -import * as DataDependencies from "../../../DataDependencies.ts" -import * as Effect from "../../../Effect.ts" -import { flatMapOption } from "../../../Effect.ts" -import * as Option from "../../../Option.ts" -import * as S from "../../../Schema.ts" -import { type Codec, NonNegativeInt } from "../../../Schema.ts" -import * as SchemaAST from "../../../SchemaAST.ts" -import { setupRequestContextFromCurrent } from "../../../setupRequest.ts" -import { type FilterArgs, getContextMap, type PersistenceModelType, type StoreConfig, storeId, StoreMaker } from "../../../Store.ts" -import type { FilterResult } from "../../filter/filterApi.ts" -import type { FieldValues } from "../../filter/types.ts" -import * as Q from "../../query.ts" -import { repositoryDependencyPaths } from "../dependency.ts" -import type { ChangeFeed, ChangeFeedEvent, Repository } from "../service.ts" -import { ValidationError, ValidationResult } from "../validation.ts" - -const dedupe = Array.dedupeWith(Equivalence.String) - -// ms buckets: dense under 100ms (common path), then mid-tail and multi-second stalls. -// Rare 0.5–1s+ encodes (fat aggregates) must not collapse into a single overflow bin. -const schemaDurationBoundaries = [ - 0.1, - 0.5, - 1, - 2, - 5, - 10, - 25, - 50, - 100, - 150, - 200, - 300, - 500, - 750, - 1_000, - 1_500, - 2_000, - 5_000 -] -// Event-loop-relevant stall floor: encodes/decodes above this are rare but block Node. -const SCHEMA_SLOW_MS = 100 -const schemaDecodeDuration = Metric.histogram("app.schema.decode.duration", { boundaries: schemaDurationBoundaries }) -const schemaEncodeDuration = Metric.histogram("app.schema.encode.duration", { boundaries: schemaDurationBoundaries }) -const schemaItemCount = Metric.histogram("app.schema.item_count", { - boundaries: [0, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1_000, 5_000] -}) -const schemaSlow = Metric.counter("app.schema.slow", { - description: `Repository schema encode/decode slower than ${SCHEMA_SLOW_MS}ms`, - incremental: true -}) - -const entityStateFromItems = (items: readonly unknown[]): string | undefined => { - const first = items[0] - if (first === null || typeof first !== "object" || !("_tag" in first)) return undefined - const tag = (first as { readonly _tag: unknown })._tag - return typeof tag === "string" ? tag : undefined -} - -const timeSchema = ( - operation: "decode" | "encode", - entity: string, - queryMode: "aggregate" | "collect" | "project" | "transform" | undefined, - itemCount: number, - entityState?: string -) => -(self: Effect.Effect): Effect.Effect => - Effect.clockWith((clock) => { - const startedAt = clock.currentTimeNanosUnsafe() - const attributes = { - "app.entity": entity, - "app.schema.operation": operation, - ...(queryMode !== undefined && { "app.query.mode": queryMode }), - ...(entityState !== undefined && { "app.entity.state": entityState }) - } - return Effect.onExit(self, () => { - const durationMs = Number(clock.currentTimeNanosUnsafe() - startedAt) / 1_000_000 - const slow = durationMs >= SCHEMA_SLOW_MS - return Effect.all([ - Effect.annotateCurrentSpan({ - [`app.schema.${operation}.duration_ms`]: durationMs, - "app.schema.item_count": itemCount, - "app.schema.slow": slow, - ...(queryMode !== undefined && { "app.query.mode": queryMode }), - ...(entityState !== undefined && { "app.entity.state": entityState }) - }), - Metric.update( - Metric.withAttributes( - operation === "decode" ? schemaDecodeDuration : schemaEncodeDuration, - attributes - ), - durationMs - ), - Metric.update(Metric.withAttributes(schemaItemCount, attributes), itemCount), - ...(slow - ? [Metric.update(Metric.withAttributes(schemaSlow, attributes), 1)] as const - : []) - ], { discard: true }) - }) - }) - -/** - * A base implementation to create a repository. - */ -export function makeRepoInternal< - Evt = never ->() { - return < - ItemType extends string, - R, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded - >( - name: ItemType, - schema: S.Codec, - mapTo: (e: Encoded, etag: string | undefined) => PersistenceModelType, - idKey: IdKey - ) => { - type PM = PersistenceModelType - function mapToPersistenceModel( - e: Encoded, - getEtag: (id: string) => string | undefined - ): PM { - return mapTo(e, getEtag(e[idKey])) - } - - function mapReverse( - { _etag, ...e }: PM, - setEtag: (id: string, eTag: string | undefined) => void - ): Encoded { - setEtag((e as any)[idKey], _etag) - // the store already applied `jitM` and decoded JSON→Encoded - return e as unknown as Encoded - } - - const mkStore = makeStore()(name, schema, mapTo, idKey) - - function make( - args: [Evt] extends [never] ? { - schemaContext?: Context.Context - makeInitial?: Effect.Effect | undefined - dependencyIds?: (item: T) => NonEmptyReadonlyArray - additionalWriteDependencies?: (item: T) => ReadonlyArray - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - schemaContext?: Context.Context - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - dependencyIds?: (item: T) => NonEmptyReadonlyArray - additionalWriteDependencies?: (item: T) => ReadonlyArray - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - ) { - return Effect - .gen(function*() { - const rctx: Context.Context = args.schemaContext ?? Context.empty() as any - const provideRctx = Effect.provide(rctx) - const encodeMany = (items: readonly T[]) => - S.encodeEffect(S.Array(schema))(items).pipe( - provideRctx, - timeSchema("encode", name, undefined, items.length, entityStateFromItems(items)) - ) - const decode = flow(S.decodeEffectConcurrently(schema), provideRctx) - const decodeMany = flow( - S.decodeEffectConcurrently(S.Array(schema)), - provideRctx - ) - - const store = yield* mkStore(args.makeInitial, args.config) - const dependencyPaths = repositoryDependencyPaths(schema) - const recordRead = DataDependencies.readRepo(name) - const entityDependency = (ids: NonEmptyReadonlyArray) => - DataDependencies.repo(name, [String(ids[0]), ...ids.slice(1).map(String)]) - const valuesAtPath = (value: unknown, path: string): readonly unknown[] => { - if (path.length === 0) return [value] - const [head, ...tail] = path.split(".") - if (head === undefined) return [value] - if (head === "-1") { - return globalThis.Array.isArray(value) - ? value.flatMap((item) => valuesAtPath(item, tail.join("."))) - : [] - } - return typeof value === "object" && value !== null - ? valuesAtPath((value as Record)[head], tail.join(".")) - : [] - } - const dependencyIds = (item: T): NonEmptyReadonlyArray => { - const configured = dependencyPaths - .flatMap((path) => valuesAtPath(item, String(path))) - .filter((value): value is string => typeof value === "string") ?? [] - const ids = args.dependencyIds?.(item) ?? [String(item[idKey]), ...configured] - return [ids[0], ...ids.slice(1)] - } - const itemDependencies = (item: T) => { - const ids = dependencyIds(item) - return [ - DataDependencies.repo(name, ids), - ...(args.additionalWriteDependencies?.(item) ?? []) - ] - } - const queryDependencyIds = (filter: readonly FilterResult[] | undefined): readonly string[] => { - if (!filter) return [] - const paths = new Set([String(idKey), ...dependencyPaths]) - const visit = (items: readonly FilterResult[]): readonly string[] => { - if (items.some((item) => item.t === "or" || item.t === "or-scope")) return [] - return items.flatMap((item) => { - if ("result" in item) return visit(item.result) - if (!paths.has(item.path)) return [] - const value: unknown = item.value - if (item.op === "eq" && typeof value === "string") return [value] - if (item.op === "in" && globalThis.Array.isArray(value)) { - return value.filter((entry): entry is string => typeof entry === "string") - } - return [] - }) - } - return [...new Set(visit(filter))] - } - const recordEntityRead = (id: T[IdKey]) => DataDependencies.read(entityDependency([id])) - const recordEntityWrite = (ids: NonEmptyReadonlyArray) => - DataDependencies.write(entityDependency(ids)) - const recordItemWrite = (items: NonEmptyReadonlyArray) => - Effect.forEach( - DataDependencies.merge(new Set(items.flatMap(itemDependencies))), - DataDependencies.write, - { discard: true } - ) - const cms = Effect.map(getContextMap.pipe(Effect.orDie), (_) => ({ - get: (id: string) => _.get(`${name}.${id}`), - set: (id: string, etag: string | undefined) => _.set(`${name}.${id}`, etag) - })) - - const pub = "publishEvents" in args - ? args.publishEvents - : () => Effect.void - - type ChangeFeedHandler = (evt: ChangeFeedEvent) => Effect.Effect - const changeFeedByNamespace = yield* Ref.make(HashMap.empty>()) - const changeFeedWildcard = yield* Ref.make(HashSet.empty()) - // clear all handlers when the repository's scope closes - yield* Effect.addFinalizer(() => - Effect.all([ - Ref.set(changeFeedByNamespace, HashMap.empty>()), - Ref.set(changeFeedWildcard, HashSet.empty()) - ], { discard: true }) - ) - const changeFeed: ChangeFeed = { - publish: ([items, op]) => - Effect.gen(function*() { - const ns = yield* storeId - const evt: ChangeFeedEvent = [items, op, ns] - const map = yield* Ref.get(changeFeedByNamespace) - const wild = yield* Ref.get(changeFeedWildcard) - const targeted = Option.getOrElse(HashMap.get(map, ns), () => HashSet.empty()) - const all = HashSet.union(targeted, wild) - yield* Effect.forEach(all, (h) => h(evt), { concurrency: "unbounded", discard: true }) - }), - subscribe: (handler, options) => { - const ns = options?.namespace - if (ns === undefined) { - return Effect.acquireRelease( - Ref.update(changeFeedWildcard, HashSet.add(handler)), - () => Ref.update(changeFeedWildcard, HashSet.remove(handler)) - ) - } - return Effect.acquireRelease( - Ref.update(changeFeedByNamespace, (m) => { - const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty()) - return HashMap.set(m, ns, HashSet.add(cur, handler)) - }), - () => - Ref.update(changeFeedByNamespace, (m) => { - const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty()) - const next = HashSet.remove(cur, handler) - return HashSet.size(next) === 0 ? HashMap.remove(m, ns) : HashMap.set(m, ns, next) - }) - ) - } - } - - const allE = cms - .pipe(Effect.flatMap((cm) => Effect.map(store.all, (_) => _.map((_) => mapReverse(_, cm.set))))) - - const all = Effect - .flatMap( - allE, - (items) => - decodeMany(items).pipe( - Effect.orDie, - timeSchema("decode", name, undefined, items.length) - ) - ) - .pipe( - Effect.tap(() => recordRead), - Effect.map((_) => _ as T[]), - Effect.withSpan("Repository.all", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - - const fieldsSchema = schema as unknown as { fields: any } - // assumes the id field never needs a service... - const i = ("fields" in fieldsSchema ? S.Struct(fieldsSchema["fields"]) as unknown as typeof schema : schema) - .pipe((_) => { - let ast = _.ast - if (ast._tag === "Declaration") ast = ast.typeParameters[0]! - - const pickIdFromAst = (a: SchemaAST.AST) => { - // Unwrap Declaration (e.g. TaggedClass) to get the underlying Objects AST - let inner = a - if (inner._tag === "Declaration") inner = inner.typeParameters[0]! - // Pick from the original AST to preserve the full encoding chain (e.g. decodeTo transformations). - // Using toEncoded would lose transformation info needed to encode Type -> Encoded. - if (SchemaAST.isObjects(inner)) { - const field = inner.propertySignatures.find((_) => _.name === idKey) - if (field) { - return S.Struct({ [idKey]: S.make(field.type) }) as unknown as Codec - } - } - return S.make(a) as unknown as Codec - } - - return ast._tag === "Union" - // we need to get the Objects (TypeLiteral), in case of class it has encoding chain... - ? S.Union( - ast.types.map((_) => pickIdFromAst(_)) - ) - : pickIdFromAst(ast) - }) - const encodeId = flow(S.encodeEffect(i), provideRctx) - const encodeIdOnly = (id: string) => - encodeId({ [idKey]: id } as any).pipe( - Effect.map((_: Record) => _[idKey as string] as Encoded[IdKey]) - ) - const findEId = Effect.fnUntraced(function*(id: Encoded[IdKey]) { - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - - return yield* Effect.flatMap( - store.find(id), - (item) => - Effect.gen(function*() { - const { set } = yield* cms - return item.pipe(Option.map((_) => mapReverse(_, set))) - }) - ) - }) - // TODO: select the particular field, instead of as struct - const findE = Effect.fnUntraced(function*(id: T[IdKey]) { - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - - return yield* pipe( - encodeId({ [idKey]: id } as any), - Effect.orDie, - Effect.map((_) => (_ as any)[idKey]), - Effect.flatMap(findEId) - ) - }) - - const loadExistingItems = (ids: ReadonlyArray) => - Effect - .forEach( - ids, - (id) => - findE(id).pipe( - Effect.flatMap(Option.match({ - onNone: () => Effect.succeed(Option.none()), - onSome: (encoded) => decode(encoded).pipe(Effect.orDie, Effect.map(Option.some)) - })) - ) - ) - .pipe(Effect.map(Array.getSomes)) - - const find = Effect.fn("Repository.find", { - kind: "client", - attributes: { "app.entity": name } - })(function*(id: T[IdKey]) { - yield* recordEntityRead(id) - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - return yield* flatMapOption(findE(id), (_) => Effect.orDie(decode(_))) - }) - - const saveAllE = (a: Iterable) => - flatMapOption( - Effect - .sync(() => toNonEmptyArray([...a])), - (a) => - Effect.gen(function*() { - const { get, set } = yield* cms - const items = a.map((_) => mapToPersistenceModel(_, get)) - const ret = yield* store.batchSet(items) - ret.forEach((_) => set(_[idKey], _._etag)) - }) - ) - .pipe(Effect.asVoid) - - const saveAll = (a: Iterable) => - encodeMany(Array.fromIterable(a)) - .pipe( - Effect.orDie, - Effect.andThen(saveAllE) - ) - - const saveAndPublish = Effect.fn("Repository.saveAndPublish", { attributes: { "app.entity": name } })( - function*(items: Iterable, events: Iterable = []) { - const it = Chunk.fromIterable(items) - if (Chunk.isNonEmpty(it)) { - const values = Chunk.toReadonlyArray(it) - const previous = args.additionalWriteDependencies || args.dependencyIds || dependencyPaths.length > 0 - ? yield* loadExistingItems(values.map((item) => item[idKey])) - : [] - yield* recordItemWrite([values[0], ...values.slice(1), ...previous]) - } - const evts = [...events] - yield* Effect.annotateCurrentSpan({ - "app.entity.ids": Chunk.map(it, (_) => _[idKey]), - "app.event.count": evts.length - }) - return yield* saveAll(it) - .pipe( - Effect.andThen(Effect.sync(() => toNonEmptyArray(evts))), - // TODO: for full consistency the events should be stored within the same database transaction, and then picked up. - (_) => flatMapOption(_, pub), - Effect.andThen(changeFeed.publish([Chunk.toArray(it), "save"] as [T[], "save" | "remove"])), - Effect.asVoid - ) - } - ) - - const removeAndPublish = Effect.fn("Repository.removeAndPublish", { attributes: { "app.entity": name } })( - function*(a: Iterable, events: Iterable = []) { - const { set } = yield* cms - const it = [...a] - if (Array.isReadonlyArrayNonEmpty(it)) { - yield* recordItemWrite(it) - } - const evts = [...events] - yield* Effect.annotateCurrentSpan({ - "app.entity.ids": it.map((_) => _[idKey]), - "app.event.count": evts.length - }) - const items = yield* encodeMany(it).pipe(Effect.orDie) - if (Array.isReadonlyArrayNonEmpty(items)) { - yield* store.batchRemove( - items.map((_) => (_[idKey])), - args.config?.partitionValue?.(items[0]) - ) - for (const e of items) { - set(e[idKey], undefined) - } - yield* Effect - .sync(() => toNonEmptyArray(evts)) - // TODO: for full consistency the events should be stored within the same database transaction, and then picked up. - .pipe((_) => flatMapOption(_, pub)) - - yield* changeFeed.publish([it, "remove"] as [T[], "save" | "remove"]) - } - } - ) - - const removeById = Effect.fn("Repository.removeById", { attributes: { "app.entity": name } })( - function*(idOrIds: T[IdKey] | ReadonlyArray) { - const ids = globalThis.Array.isArray(idOrIds) - ? idOrIds as readonly T[IdKey][] - : [idOrIds as T[IdKey]] - if (!Array.isReadonlyArrayNonEmpty(ids)) { - return - } - yield* recordEntityWrite(ids) - if (args.additionalWriteDependencies || args.dependencyIds || dependencyPaths.length > 0) { - const previous = yield* loadExistingItems(ids) - if (Array.isReadonlyArrayNonEmpty(previous)) yield* recordItemWrite(previous) - } - const { set } = yield* cms - const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie) - yield* Effect.annotateCurrentSpan({ "app.entity.ids": eids }) - yield* store.batchRemove(eids) - for (const id of eids) { - set(id, undefined) - } - yield* changeFeed.publish([[], "remove"] as [T[], "save" | "remove"]) - } - ) - - const parseMany = Effect.fnUntraced( - function*(items: readonly PM[]) { - const cm = yield* cms - return yield* decodeMany(items.map((_) => mapReverse(_, cm.set))).pipe( - Effect.orDie, - timeSchema("decode", name, "transform", items.length) - ) - } - ) - const decodeManyCache = new WeakMap< - S.Codec, - (i: readonly any[]) => Effect.Effect - >() - const getDecodeMany = (s: S.Codec) => { - let dec = decodeManyCache.get(s) - if (!dec) { - dec = S.decodeEffectConcurrently(S.Array(s)) - decodeManyCache.set(s, dec) - } - return dec - } - const parseMany2 = Effect.fnUntraced( - function*(items: readonly PM[], schema: S.Codec) { - const cm = yield* cms - return yield* getDecodeMany(schema)(items.map((_) => mapReverse(_, cm.set))).pipe( - Effect.orDie, - timeSchema("decode", name, "transform", items.length) - ) - } - ) - const filter = (args: FilterArgs) => - store - .filter( - // always enforce id and _etag because they are system fields, required for etag tracking etc - { - ...args, - select: args.select - ? dedupe([...args.select, idKey, "_etag" as any]) - : undefined - } as typeof args - ) - .pipe( - Effect.tap((items) => - Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded)[idKey], (_ as PM)._etag))) - ) - ) - - // TODO: For raw we should use S.from, and drop the R... - const query: { - ( - q: Q.QueryProjection - ): Effect.Effect> - ( - q: Q.QAll, NoInfer, A, R> - ): Effect.Effect> - } = ((q: Q.QAll) => { - const a = Q.toFilter(q, schema) - const scopedIds = queryDependencyIds(a.filter) - const recordQueryRead = Array.isReadonlyArrayNonEmpty(scopedIds) - ? DataDependencies.read(DataDependencies.repo(name, scopedIds)) - : recordRead - // Mode dispatch — see `Q.project` JSDoc for the contract: - // aggregate: GROUP BY + aggregate functions at DB level; decode raw rows with schema; SchemaError surfaces. - // project : decode raw encoded rows with schema; no PM reverse-mapping; SchemaError surfaces. - // collect : same as project, but schema yields Option and None rows are dropped. - // transform: PM reverse-map (re-inject _etag/PM state from cms cache) then decode; orDie. - const eff = a.mode === "aggregate" - ? store - // `a.select` contains `{ key, aggregate }` items not expressible in FilterFunc's - // `U extends keyof Encoded` generic. Cast is unavoidable until FilterFunc supports aggregate mode. - .filter(a as any) - // Decode raw aggregate rows directly — no PM reverse-mapping, no id/_etag needed. - .pipe( - Effect.andThen( - (items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe( - provideRctx, - timeSchema("decode", name, "aggregate", items.length) - ) - ) - ) - : a.mode === "project" - ? filter(a) - // TODO: mapFrom but need to support per field and dependencies - .pipe( - Effect.andThen( - (items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe( - provideRctx, - timeSchema("decode", name, "project", items.length) - ) - ) - ) - : a.mode === "collect" - ? filter(a) - // TODO: mapFrom but need to support per field and dependencies - .pipe( - Effect.flatMap((items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema)))(items).pipe( - Effect.map(Array.getSomes), - provideRctx, - timeSchema("decode", name, "collect", items.length) - ) - ) - ) - : Effect.flatMap( - filter(a), - (_) => - Unify.unify( - a.schema - // TODO: partial may not match? - ? parseMany2(_ as any, a.schema as any) - : parseMany(_ as any) - ) - ) - return pipe( - a.ttype === "one" - ? Effect.flatMap( - eff, - flow( - Array.head, - Option.match({ - onNone: () => Effect.fail(new NotFoundError({ id: "query", /* TODO */ type: name })), - onSome: Effect.succeed - }) - ) - ) - : a.ttype === "count" - ? Effect - .map(eff, (_) => NonNegativeInt(_.length)) - .pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))) - : eff, - Effect.tap((r) => - Effect.annotateCurrentSpan({ - "app.query.ttype": a.ttype, - "app.query.mode": a.mode, - "db.response.returned_rows": Array.isArray(r) ? r.length : 1 - }) - ), - Effect.tap(() => recordQueryRead), - Effect.withSpan("Repository.query", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - }) as any - - const validateSample = Effect.fn("Repository.validateSample", { attributes: { "app.entity": name } })( - function*(options?: { - percentage?: number - maxItems?: number - }) { - const percentage = options?.percentage ?? 0.1 // default 10% - const maxItems = options?.maxItems - - // 1. get all IDs with projection (bypasses main schema decode) - const allIds = yield* store - .filter({ - t: null as unknown as Encoded, - select: [idKey as keyof Encoded] - }) - .pipe( - // a projection never decodes a whole document, so the store's - // document decode cannot fail here - Effect.catchTag("SchemaError", (e) => Effect.die(e)), - Effect.withSpan("Repository.filter", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - - // 2. random subset - const shuffled = [...allIds].sort(() => Math.random() - 0.5) - const sampleSize = Math.min( - maxItems ?? Infinity, - Math.ceil(allIds.length * percentage) - ) - const sample = shuffled.slice(0, sampleSize) - - // 3. validate each item - const errors: ValidationError[] = [] - - for (const item of sample) { - const id = item[idKey] - const stored = yield* store.find(id).pipe( - Effect.result, - Effect.withSpan("Repository.find", { - kind: "client", - attributes: { "app.entity": name, "app.entity.id": id } - }, { captureStackTrace: false }) - ) - - // a store-boundary failure: `defaultValues` + `jitM` did not - // produce a document the Encoded shape accepts, so the store - // failed with `S.SchemaError` instead of returning one. Report - // that document and keep sampling - it used to kill the run. - if (Result.isFailure(stored)) { - const failure = stored.failure - if (!S.isSchemaError(failure)) return yield* Effect.fail(failure) - errors.push( - ValidationError.make({ - id, - // the store failed before it could hand back a document - rawData: undefined, - jitMResult: undefined, - error: failure - }) - ) - continue - } - - const rawResult = stored.success - if (Option.isNone(rawResult)) continue - - // the store merged `defaultValues`, applied `jitM` on the raw - // JSON document and decoded JSON→Encoded, so this is exactly - // what the repository decodes into the domain type. - const rawData = rawResult.value as Encoded - - // the repository's own decode of a document the store accepted - const decodeResult = yield* S.decodeEffectConcurrently(schema)(rawData).pipe( - Effect.result, - provideRctx - ) - - if (Result.isFailure(decodeResult)) { - errors.push( - ValidationError.make({ - id, - rawData, - jitMResult: rawData, - error: decodeResult.failure - }) - ) - } - } - - return ValidationResult.make({ - total: NonNegativeInt(allIds.length), - sampled: NonNegativeInt(sample.length), - valid: NonNegativeInt(sample.length - errors.length), - errors - }) - } - ) - - // The store fails with `S.SchemaError` when a stored document does not - // decode - see `Store.all` / `Store.find`. The internals above let that - // failure propagate; producing the public members is the single - // boundary where it becomes a defect, because these signatures declare - // only `DatabaseError`. `queryRaw`, `query` and `mapped` already - // declare `S.SchemaError`, and `validateSample` reports it per - // document instead of dying. - const r = { - changeFeed, - itemType: name, - idKey, - find: (id: T[IdKey]) => find(id).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - all: all.pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - saveAndPublish: (items: Iterable, events?: Iterable) => - saveAndPublish(items, events).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - removeAndPublish, - removeById: (idOrIds: T[IdKey] | ReadonlyArray) => - removeById(idOrIds).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - seedNamespace: (namespace: string) => store.seedNamespace(namespace), - validateSample, - queryRaw(schema: S.Codec, q: Q.RawQuery) { - return store.queryRaw(q).pipe( - Effect.flatMap((items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(schema)))(items as readonly S.Json[]).pipe( - timeSchema("decode", name, undefined, items.length) - ) - ), - Effect.tap(() => recordRead), - Effect.withSpan("Repository.queryRaw", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - }, - query(q: any) { - // eslint-disable-next-line prefer-rest-params - return query(typeof q === "function" ? Pipeable.pipeArguments(Q.make(), arguments) : q) as any - }, - /** - * @internal - */ - mapped: (schema: S.Codec) => { - const dec = S.decodeEffectConcurrently(schema) - const encMany = S.encodeEffect(S.Array(schema)) - const decMany = S.decodeEffectConcurrently(S.Array(schema)) - const spanAttrs = { kind: "client" as const, attributes: { "app.entity": name } } - return { - all: allE.pipe( - Effect.flatMap((items) => - decMany(items).pipe( - timeSchema("decode", name, undefined, items.length) - ) - ), - Effect.tap(() => recordRead), - Effect.map((_) => _ as any[]), - Effect.withSpan("Repository.mapped.all", spanAttrs, { captureStackTrace: false }) - ), - find: (id: T[IdKey]) => - flatMapOption(findE(id), dec).pipe( - Effect.tap(() => recordEntityRead(id)), - Effect.withSpan("Repository.mapped.find", { - ...spanAttrs, - attributes: { ...spanAttrs.attributes, "app.entity.id": id } - }, { captureStackTrace: false }) - ), - // query: (q: any) => { - // const a = Q.toFilter(q) - - // return filter(a) - // .pipe( - // Effect.flatMap(decMany), - // Effect.map((_) => _ as any[]), - // Effect.withSpan("Repository.mapped.query [effect-app/infra]", { - // captureStackTrace: false, - // attributes: { - // "repository.model_name": name, - // query: { ...a, schema: a.schema ? "__SCHEMA__" : a.schema, filter: a.filter.build() } - // } - // }) - // ) - // }, - save: (...xes: any[]) => - Effect - .flatMap( - encMany(xes).pipe( - timeSchema("encode", name, undefined, xes.length, entityStateFromItems(xes)) - ), - (_) => saveAllE(_) - ) - .pipe( - Effect.tap(() => - Array.isReadonlyArrayNonEmpty(xes) - ? recordEntityWrite([xes[0][idKey], ...xes.slice(1).map((_) => _[idKey])]) - : Effect.void - ), - Effect.withSpan("Repository.mapped.save", spanAttrs, { captureStackTrace: false }) - ) - } - } - } - return r as Repository, RPublish, RCtx> - }) - .pipe(Effect - // .withSpan("Repository.make [effect-app/infra]", { attributes: { "repository.model_name": name } }) - .withLogSpan("Repository.make: " + name)) - } - - return { - make, - Q: Q.make() - } - } -} - -const pluralize = (s: string) => - s.endsWith("s") - ? s + "es" - : s.endsWith("y") - ? s.substring(0, s.length - 1) + "ies" - : s + "s" - -export function makeStore() { - return < - ItemType extends string, - R, - E, - T, - IdKey extends keyof Encoded - >( - name: ItemType, - schema: S.Codec, - mapTo: (e: E, etag: string | undefined) => Encoded, - idKey: IdKey - ) => { - function makeStore( - makeInitial?: Effect.Effect, - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - ) { - function encodeToEncoded() { - const getEtag = () => undefined - return (t: T) => - S.encodeEffect(schema)(t).pipe( - Effect.orDie, - Effect.map((_) => mapToPersistenceModel(_, getEtag)) - ) - } - - function mapToPersistenceModel( - e: E, - getEtag: (id: string) => string | undefined - ): Encoded { - return mapTo(e, getEtag((e as any)[idKey] as string)) - } - - return Effect.gen(function*() { - const { make } = yield* StoreMaker - - const store = yield* make( - pluralize(name), - idKey, - makeInitial - ? makeInitial - .pipe( - Effect.flatMap(Effect.forEach(encodeToEncoded())), - setupRequestContextFromCurrent("Repository.makeInitial [effect-app/infra]", { - attributes: { "app.entity": name } - }) - ) - : undefined, - { - ...config, - schema, - partitionValue: config?.partitionValue - ?? ((_) => "primary") /*(isIntegrationEvent(r) ? r.companyId : r.id*/ - } - ) - - return store - }) - } - - return makeStore - } -} - -export interface Repos< - T, - Encoded extends { id: string }, - RSchema, - Evt, - ItemType extends string, - IdKey extends keyof T, - RPublish -> { - make( - args: [Evt] extends [never] ? { - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - ): Effect.Effect< - Repository, - E, - StoreMaker | RInitial | R2 | Scope.Scope - > - makeWith( - args: [Evt] extends [never] ? { - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - }, - f: (r: Repository) => Out - ): Effect.Effect - readonly Q: ReturnType> - readonly type: Repository -} diff --git a/packages/effect-app/src/Model/Repository/legacy.ts b/packages/effect-app/src/Model/Repository/legacy.ts deleted file mode 100644 index bac20bb3e..000000000 --- a/packages/effect-app/src/Model/Repository/legacy.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { DatabaseError, OptimisticConcurrencyException } from "../../client/errors.ts" -import type * as Effect from "../../Effect.ts" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" - -export interface Mapped1 { - all: Effect.Effect - save: (...xes: readonly A[]) => Effect.Effect - find: (id: A[IdKey]) => Effect.Effect, S.SchemaError | DatabaseError, R> -} - -// TODO: auto use project, and select fields from the From side of schema only -export interface Mapped2 { - all: Effect.Effect -} - -export interface Mapped { - (schema: S.Codec): Mapped1 - // TODO: constrain on Encoded2 having to contain only fields that fit Encoded - (schema: S.Codec): Mapped2 -} - -export interface MM { - (schema: S.Codec): Effect.Effect, never, Repo> - // TODO: constrain on Encoded2 having to contain only fields that fit Encoded - (schema: S.Codec): Effect.Effect, never, Repo> -} diff --git a/packages/effect-app/src/Model/Repository/makeRepo.ts b/packages/effect-app/src/Model/Repository/makeRepo.ts deleted file mode 100644 index 30979b03b..000000000 --- a/packages/effect-app/src/Model/Repository/makeRepo.ts +++ /dev/null @@ -1,168 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-object-type */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -// import type { ParserEnv } from "../../Schema/custom/Parser.ts" - -import type * as Scope from "effect/Scope" -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import type * as Context from "../../Context.ts" -import type * as DataDependencies from "../../DataDependencies.ts" -import * as Effect from "../../Effect.ts" -import type * as S from "../../Schema.ts" -import type { JsonRecord, StoreConfig, StoreMaker } from "../../Store.ts" -import type { FieldValues } from "../filter/types.ts" -import { type ExtendedRepository, extendRepo } from "./ext.ts" -import { makeRepoInternal } from "./internal/internal.ts" -import { RepositoryRegistry } from "./Registry.ts" -import type { Repository } from "./service.ts" - -export interface RepositoryOptions< - IdKey extends keyof T & keyof Encoded, - Encoded extends FieldValues, - T, - ItemType extends string, - Evt = never, - RPublish = never, - E = never, - RInitial = never, - RCtx = never, - RSchema = never -> { - /** - * Specify the idKey of the Type side, if it's different from the default "id". - * Does not change the Encoded side, which is always "id" to support database drivers. - * At this time as queries are operating on the Encoded side, the queries must still specify "id" regardless. - */ - idKey: IdKey - /** - * just in time Migration: for complex migrations that aren't just default simple values - * use the config.defaultValues instead for simple default values - * - * Runs at the **store boundary** on the raw JSON document, after - * `config.defaultValues` have been merged in and *before* the JSON→Encoded - * decode. It sees the document as stored - a `Date` field as an ISO string, - * a `ReadonlyMap` as an array of pairs, an explicit `null` as `null` - and - * must return JSON again, not native Encoded values. - * - * @see StoreConfig.jitM - */ - jitM?: (json: JsonRecord) => JsonRecord - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - /** - * Optional handler to be able to publish events after successfull save. - */ - publishEvents?: (evt: NonEmptyReadonlyArray) => Effect.Effect - /** - * Optional creator for initial data in the table when it's created for the first itme. - */ - makeInitial?: Effect.Effect | undefined - /** - * Optional context to be provided to Schema decode/encode. - * Useful for effectful transformations like XWithItems, where items is a transformation retrieving elements from another database table or other source. - */ - schemaContext?: Context.Context - - /** IDs, including aliases, that identify an entity for dependency invalidation. */ - dependencyIds?: (item: T) => NonEmptyReadonlyArray - - /** - * Additional derived queries or resources affected by writing an item. - * Saves invalidate dependencies derived from both the previous and next item; - * removals derive them from the removed item. - */ - additionalWriteDependencies?: (item: T) => ReadonlyArray - - overrides?: ( - repo: Repository, RPublish, RCtx> - ) => Repository, RPublish, RCtx> -} - -/** - * Create a repository instance. - * @param itemType an identifier used for the table name and e.g NotFoundError - * @param schema the Schema used for this Repository - * @param options @see RepositoryOptions - * @returns a Repository - */ -export const makeRepo: { - < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded, - E = never, - Evt = never, - RInitial = never, - RPublish = never, - RCtx = never - >( - itemType: ItemType, - schema: S.Codec, - options: RepositoryOptions - ): Effect.Effect< - ExtendedRepository, RPublish, RCtx>, - E, - RInitial | StoreMaker | RepositoryRegistry | Scope.Scope - > - < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T extends { id: unknown }, - E = never, - Evt = never, - RInitial = never, - RPublish = never, - RCtx = never - >( - itemType: ItemType, - schema: S.Codec, - options: Omit, "idKey"> - ): Effect.Effect< - ExtendedRepository, RPublish, RCtx>, - E, - RInitial | StoreMaker | RepositoryRegistry | Scope.Scope - > -} = < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded, - E = never, - RInitial = never, - RPublish = never, - Evt = never, - RCtx = never ->( - itemType: ItemType, - schema: S.Codec, - options: Omit, "idKey"> & { - idKey?: IdKey - } -) => - Effect.gen(function*() { - const mkRepo = makeRepoInternal()( - itemType, - schema, - (e, _etag) => ({ ...e, _etag }), - options.idKey ?? "id" as any - ) - // `jitM` belongs to the store: it migrates the raw JSON document before the - // store decodes JSON→Encoded, so it travels as part of the store config. - const { jitM, ...rest } = options - let r = yield* mkRepo.make( - (jitM ? { ...rest, config: { ...rest.config, jitM } } : rest) as any - ) - if (options.overrides) r = options.overrides(r) - const repo = extendRepo(r) - const registry = yield* RepositoryRegistry - registry.register(itemType, repo) - return repo - }) diff --git a/packages/effect-app/src/Model/Repository/service.ts b/packages/effect-app/src/Model/Repository/service.ts deleted file mode 100644 index 875a0cd17..000000000 --- a/packages/effect-app/src/Model/Repository/service.ts +++ /dev/null @@ -1,676 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Scope from "effect/Scope" -import type { DatabaseError, InvalidStateError, NotFoundError, OptimisticConcurrencyException } from "../../client/errors.ts" -import type * as Effect from "../../Effect.ts" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" -import type { NonNegativeInt } from "../../Schema/numbers.ts" -import type { FieldValues, IsNever, ResolveFirstLevel } from "../filter/types.ts" -import type { QAll, Query, QueryProjection, RawQuery } from "../query.ts" -import type { Mapped } from "./legacy.ts" -import type { ValidationResult } from "./validation.ts" - -/** - * Event emitted by a repository's ChangeFeed. - * - * Tuple shape: `[items, op, namespace]` — `namespace` is the `storeId` - * context value at publish time (defaults to `"primary"`). - */ -export type ChangeFeedEvent = readonly [items: T[], op: "save" | "remove", namespace: string] - -/** - * Synchronous broadcast channel for repository change events. - * - * `publish` only completes after every matching handler's Effect has - * finished — callers must await all handlers before continuing. Subscribers - * can target a single namespace via `options.namespace`, or omit it to - * receive events from every namespace (wildcard). - * - * Handlers are auto-removed when the subscriber's Scope closes; the full - * handler set is cleared when the repository's own scope closes. - */ -export interface ChangeFeed { - readonly publish: (evt: [T[], "save" | "remove"]) => Effect.Effect - readonly subscribe: ( - handler: (evt: ChangeFeedEvent) => Effect.Effect, - options?: { readonly namespace?: string } - ) => Effect.Effect -} - -export interface Repository< - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T, - RSchema, - RPublish, - RProvided = never -> { - readonly itemType: ItemType - readonly idKey: IdKey - readonly find: (id: T[IdKey]) => Effect.Effect, DatabaseError, RSchema> - readonly all: Effect.Effect - readonly saveAndPublish: ( - items: Iterable, - events?: Iterable - ) => Effect.Effect - readonly changeFeed: ChangeFeed - readonly removeAndPublish: ( - items: Iterable, - events?: Iterable - ) => Effect.Effect - - readonly removeById: ( - idOrIds: T[IdKey] | ReadonlyArray - ) => Effect.Effect - - readonly queryRaw: ( - schema: S.Codec, - raw: RawQuery - ) => Effect.Effect - - /** - * Explicitly seed a namespace. Primary is seeded eagerly on initialization. - * Non-primary namespaces must be seeded explicitly before use. - */ - readonly seedNamespace: (namespace: string) => Effect.Effect - - readonly query: { - // ending with projection - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - E extends boolean = false - >( - q: ( - initial: Query - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: ( - _: $A - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: ( - _: $B - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: ( - _: $C - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: ( - _: $D - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - $I = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => $I, - q10: (_: $I) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - - // ending with generic query - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - E extends boolean = false - >( - q: (initial: Query) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: ( - _: $A - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: ( - _: $A - ) => $B, - q3: ( - _: $B - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: ( - _: $C - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: ( - _: $D - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: ( - _: $E - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: ( - _: $F - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: ( - _: $G - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: ( - _: $H - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - $I = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => $I, - q10: ( - _: $I - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - } - - /** @deprecated use query */ - readonly mapped: Mapped - - /** - * Validates a random sample of repository items by applying jitM and schema decode. - * Useful for testing that migrations and schema changes work correctly on existing data. - */ - readonly validateSample: (options?: { - /** percentage of items to sample (0.0-1.0), default 0.1 (10%) */ - percentage?: number - /** optional maximum number of items to sample */ - maxItems?: number - }) => Effect.Effect -} - -type DistributeQueryIfExclusiveOnArray = [Exclusive] extends [true] - ? T extends any ? RefineTHelper extends infer R ? IsNever extends true ? never - : readonly R[] - : never - : never - : readonly RefineTHelper[] - -type NullableRefined = { - // EncodedRefined may be a union, so if you just keyof you'll get just common keys - // p.s. NullableRefined is homomorphic in T so it distributes itself over T - [k in keyof T]: [null] extends [T[k]] ? [null] extends [Extract[k]] ? T[k] - : Exclude - : T[k] -} - -// for DX purposes -type ShouldRecursiveExtractTagged = true extends { - [k in keyof T]: "_tag" extends keyof T[k] - ? k extends keyof EncodedRefined ? "_tag" extends keyof EncodedRefined[k] ? true - : false - : false - : false -}[keyof T] ? true - : false - -// support is not 100% but we have to make compromises -type RecursiveExtractTagged = ShouldRecursiveExtractTagged extends true ? { - [k in keyof T]: "_tag" extends keyof T[k] - ? k extends keyof EncodedRefined ? "_tag" extends keyof EncodedRefined[k] ? ExtractTagged - : T[k] - : T[k] - : T[k] - } - : T - -type ExtractTagged_ = EncodedRefined extends { _tag: string } - ? T extends { _tag: string } ? Extract - : T - : T - -type ExtractTagged = ExtractTagged_ extends infer R - ? RecursiveExtractTagged, EncodedRefined> - : never - -type ShouldRecursiveExtractIded = true extends { - [k in keyof T]: "id" extends keyof T[k] ? k extends keyof EncodedRefined ? "id" extends keyof EncodedRefined[k] ? true - : false - : false - : false -}[keyof T] ? true - : false - -type RecusiveExtractIded = ShouldRecursiveExtractIded extends true ? { - [k in keyof T]: "id" extends keyof T[k] - ? k extends keyof EncodedRefined ? "id" extends keyof EncodedRefined[k] ? ExtractIded - : T[k] - : T[k] - : T[k] - } - : T - -type ExtractIded_ = EncodedRefined extends { id: string } - ? T extends { id: string } ? Extract - : T - : T - -type ExtractIded = ExtractIded_ extends infer R - ? RecusiveExtractIded, EncodedRefined> - : never - -export type RefineTHelper = ResolveFirstLevel< - NullableRefined< - ExtractIded, EncodedRefined>, - EncodedRefined - > -> diff --git a/packages/effect-app/src/Model/Repository/validation.ts b/packages/effect-app/src/Model/Repository/validation.ts deleted file mode 100644 index 25b34b058..000000000 --- a/packages/effect-app/src/Model/Repository/validation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as S from "../../Schema.ts" -import { NonNegativeInt } from "../../Schema.ts" - -/** - * Represents a single validation error when decoding a repository item. - * Contains full context for debugging: the stored data and the decode error. - */ -export class ValidationError extends S.Opaque()(S.Struct({ - /** the id of the item that failed validation */ - id: S.Unknown, - /** - * the data as returned by the store: `defaultValues` merged, `jitM` applied - * to the raw JSON document, and JSON decoded to the Encoded shape - */ - rawData: S.Unknown, - /** - * @deprecated identical to {@link rawData}. `jitM` now runs inside the store, - * before the JSON→Encoded decode, so the repository never sees the document - * as it was before the migration. - */ - jitMResult: S.Unknown, - /** the S.SchemaError from schema decode */ - error: S.Unknown -})) {} - -/** - * Result of validating a sample of repository items. - */ -export class ValidationResult extends S.Opaque()(S.Struct({ - /** total number of items in the repository */ - total: NonNegativeInt, - /** number of items that were sampled for validation */ - sampled: NonNegativeInt, - /** number of items that passed validation */ - valid: NonNegativeInt, - /** list of validation errors with full context */ - errors: S.Array(ValidationError) -})) {} diff --git a/packages/effect-app/src/Model/dsl.ts b/packages/effect-app/src/Model/dsl.ts deleted file mode 100644 index a99abfd71..000000000 --- a/packages/effect-app/src/Model/dsl.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { NonEmptyReadonlyArray } from "../Array.ts" -import { toNonEmptyArray } from "../Array.ts" -import * as Effect from "../Effect.ts" -import { type FixEnv, get, logMany, type Pure, type PureEnvEnv, type PureLogT, set } from "../Pure.ts" - -export interface PureDSL { - get: Pure - set: (s: S2) => Pure - log: (...w: W[]) => PureLogT -} - -export const AnyPureDSL: PureDSL = { - get: get(), - set: set as any, - log: (...evt: any[]) => logMany(evt) -} - -const anyDSL = makeDSL() - -export type AllDSL = - & (( - pure: (dsl: PureDSL) => Effect.Effect - ) => Effect.Effect>) - & AllDSLExt - -export interface AllDSLExt { - modify: ( - pure: (items: readonly S1[], dsl: PureDSL) => Effect.Effect - ) => Effect.Effect> - update: ( - pure: (items: readonly S1[], log: (...evt: Evt[]) => PureLogT) => Effect.Effect - ) => Effect.Effect> - updateWith: ( - upd: (item: readonly S1[]) => readonly S2[] - ) => Effect.Effect> -} - -export function makeAllDSL() { - const dsl: AllDSL = anyDSL - return dsl -} - -export type OneDSL = - & (( - pure: (dsl: PureDSL) => Effect.Effect - ) => Effect.Effect>) - & OneDSLExt - -export interface OneDSLExt { - modify: ( - pure: (items: S1, dsl: PureDSL) => Effect.Effect - ) => Effect.Effect | PureEnvEnv> - update: ( - pure: (items: S1, log: (...evt: Evt[]) => PureLogT) => Effect.Effect - ) => Effect.Effect> - updateWith: ( - upd: (item: S1) => S2 - ) => Effect.Effect> -} - -export function makeOneDSL(): OneDSL { - return anyDSL -} - -export function makeDSL() { - const dsl: PureDSL = AnyPureDSL - - function modify< - R, - E, - A - >( - pure: ( - items: S1, - dsl: PureDSL - ) => Effect.Effect - ): Effect.Effect> { - return dsl.get.pipe(Effect.flatMap((items) => pure(items, dsl))) as any - } - - function update< - R, - E - >( - pure: ( - items: S1, - log: (...evt: Evt[]) => PureLogT - ) => Effect.Effect - ): Effect.Effect> { - return dsl.get.pipe(Effect.flatMap((items) => pure(items, dsl.log).pipe(Effect.tap(dsl.set)))) as any - } - - function withDSL< - R, - A, - E - >( - pure: (dsl: PureDSL) => Effect.Effect - ): Effect.Effect> { - return pure(AnyPureDSL) as any - } - - function updateWith(upd: (item: S1) => S2) { - return update((_: S1) => Effect.sync(() => upd(_))) - } - - return Object.assign( - withDSL, - { - modify, - update, - updateWith - } - ) -} - -export interface DSLExt extends ReturnType> {} - -export function ifAny(fn: (items: NonEmptyReadonlyArray) => Effect.Effect) { - return (items: Iterable) => Effect.flatMapOption(Effect.sync(() => toNonEmptyArray([...items])), fn) -} - -export function ifAny_( - items: Iterable, - fn: (items: NonEmptyReadonlyArray) => Effect.Effect -) { - return Effect.flatMapOption(Effect.sync(() => toNonEmptyArray([...items])), fn) -} diff --git a/packages/effect-app/src/Model/filter/filterApi.ts b/packages/effect-app/src/Model/filter/filterApi.ts deleted file mode 100644 index 81c5eb250..000000000 --- a/packages/effect-app/src/Model/filter/filterApi.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { type RelationDirection } from "../query.ts" - -export type InOps = - | "in" - | "notIn" - -export type OtherOps = - | "endsWith" - | "startsWith" - | "notEndsWith" - | "notStartsWith" - | "contains" - | "notContains" - | "includes" - | "notIncludes" - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all" - | "hasKey" - | "notHasKey" - | "hasValue" - | "notHasValue" - | "hasKeyValue" - | "notHasKeyValue" - | "hasKey-any" - | "notHasKey-any" - | "hasKey-all" - | "notHasKey-all" - | "hasValue-any" - | "notHasValue-any" - | "hasValue-all" - | "notHasValue-all" - | "hasKeyValue-any" - | "notHasKeyValue-any" - | "hasKeyValue-all" - | "notHasKeyValue-all" - | "eq" - | "neq" - | "gt" - | "gte" - | "lt" - | "lte" - -export type Ops = OtherOps | InOps - -export type FilterScopes = { - t: "or-scope" - result: readonly FilterResult[] - relation: RelationDirection -} | { - t: "and-scope" - result: readonly FilterResult[] - relation: RelationDirection -} | { - t: "where-scope" - result: readonly FilterResult[] - relation: RelationDirection -} - -export type FilterR = { - op: Ops - - path: string - value: unknown -} - -export type FilterResult = - | { - t: "where" - } & FilterR - | { - t: "or" - } & FilterR - | { - t: "and" - } & FilterR - | FilterScopes diff --git a/packages/effect-app/src/Model/filter/types.ts b/packages/effect-app/src/Model/filter/types.ts deleted file mode 100644 index d4cca62cf..000000000 --- a/packages/effect-app/src/Model/filter/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -// codegen:start {preset: barrel, include: ./types/*.ts} -export * from "./types/errors.ts" -export * from "./types/fields.ts" -export * from "./types/utils.ts" -export * from "./types/validator.ts" -// codegen:end diff --git a/packages/effect-app/src/Model/filter/types/errors.ts b/packages/effect-app/src/Model/filter/types/errors.ts deleted file mode 100644 index f6501cb94..000000000 --- a/packages/effect-app/src/Model/filter/types/errors.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { FieldValues, InternalFieldName } from "./fields.ts" -import type { BrowserNativeObject, IsAny, LiteralUnion, Merge } from "./utils.ts" -import type { RegisterOptions, ValidateResult } from "./validator.ts" - -export type Message = string - -export type MultipleFieldErrors = - & { - [K in keyof RegisterOptions]?: ValidateResult - } - & { - [key: string]: ValidateResult - } - -export type FieldError = { - type: LiteralUnion - root?: FieldError - // ref?: Ref - types?: MultipleFieldErrors - message?: Message -} - -export type ErrorOption = { - message?: Message - type?: LiteralUnion - types?: MultipleFieldErrors -} - -export type DeepRequired = T extends BrowserNativeObject | Blob ? T - : { - [K in keyof T]-?: NonNullable> - } - -export type FieldErrorsImpl = { - [K in keyof T]?: T[K] extends BrowserNativeObject | Blob ? FieldError - : T[K] extends object ? Merge> - : FieldError -} - -export type FieldErrors = Partial< - FieldValues extends IsAny ? any - : FieldErrorsImpl> -> - -export type InternalFieldErrors = Partial< - Record -> diff --git a/packages/effect-app/src/Model/filter/types/fields.ts b/packages/effect-app/src/Model/filter/types/fields.ts deleted file mode 100644 index 73808a451..000000000 --- a/packages/effect-app/src/Model/filter/types/fields.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { IsFlatObject } from "./utils.ts" -import type { RegisterOptions } from "./validator.ts" - -export type InternalFieldName = string - -export type FieldName = IsFlatObject extends true - ? Extract - : string - -// export type CustomElement = { -// name: FieldName -// type?: string -// value?: any -// disabled?: boolean -// checked?: boolean -// options?: HTMLOptionsCollection -// files?: FileList | null -// focus?: Noop -// } - -export type FieldValue = TFieldValues[InternalFieldName] - -export type FieldValues = Record - -export type NativeFieldValue = - | string - | number - | boolean - | null - | undefined - | unknown[] - -// export type FieldElement = -// | HTMLInputElement -// | HTMLSelectElement -// | HTMLTextAreaElement -// | CustomElement - -// export type Ref = FieldElement - -export type Field = { - _f: { - // ref: Ref - name: InternalFieldName - // refs?: HTMLInputElement[] - mount?: boolean - } & RegisterOptions -} - -export type FieldRefs = Partial> diff --git a/packages/effect-app/src/Model/filter/types/path/common.ts b/packages/effect-app/src/Model/filter/types/path/common.ts deleted file mode 100644 index 40324c0d0..000000000 --- a/packages/effect-app/src/Model/filter/types/path/common.ts +++ /dev/null @@ -1,404 +0,0 @@ -import type { IsAny, IsNever } from "../utils.ts" - -/** - * Type alias to `string` which describes a lodash-like path through an object. - * E.g. `'foo.bar.0.baz'` - */ -export type PathString = string - -/** - * Type which can be traversed through with a {@link PathString}. - * I.e. objects, arrays, and tuples - */ -export type Traversable = object - -/** - * Type to query whether an array type T is a tuple type. - * @typeParam T - type which may be an array or tuple - * @example - * ``` - * IsTuple<[number]> = true - * IsTuple = false - * ``` - */ -export type IsTuple> = number extends T["length"] ? false - : true - -/** - * Type which can be used to index an array or tuple type. - */ -export type ArrayKey = number - -/** - * Type which can be used to index an object. - */ -export type Key = string - -/** - * Type to assert that a type is a {@link Key}. - * @typeParam T - type which may be a {@link Key} - */ -export type AsKey = Extract - -/** - * Type to convert a type to a {@link Key}. - * @typeParam T - type which may be converted to a {@link Key} - */ -export type ToKey = T extends ArrayKey ? `${T}` : AsKey - -/** - * Type which describes a path through an object - * as a list of individual {@link Key}s. - */ -export type PathTuple = Key[] - -/** - * Type to assert that a type is a {@link PathTuple}. - * @typeParam T - type which may be a {@link PathTuple} - */ -export type AsPathTuple = Extract - -/** - * Type to intersect a union type. - * See https://fettblog.eu/typescript-union-to-intersection/ - * @typeParam U - union - * @example - * ``` - * UnionToIntersection<{ foo: string } | { bar: number }> - * = { foo: string; bar: number } - * ``` - */ -export type UnionToIntersection = ( - U extends any ? (_: U) => any : never -) extends (_: infer I) => any ? I - : never - -/** - * Type which appends a {@link Key} to the {@link PathTuple} only if it is not - * blank, i.e. not the empty string. - * @typeParam PT - path - * @typeParam K - key - * @example - * ``` - * AppendNonBlankKey<['foo'], 'bar'> = ['foo', 'bar'] - * AppendNonBlankKey<['foo'], ''> = ['foo'] - * ``` - */ -type AppendNonBlankKey = K extends "" ? PT - : [...PT, K] - -/** - * Type to implement {@link SplitPathString} tail recursively. - * @typeParam PS - remaining {@link PathString} which should be split into its - * individual {@link Key}s - * @typeParam PT - accumulator of the {@link Key}s which have been split from - * the original {@link PathString} already - */ -type SplitPathStringImpl< - PS extends PathString, - PT extends PathTuple -> = PS extends `${infer K}.${infer R}` ? SplitPathStringImpl> - : AppendNonBlankKey - -/** - * Type to split a {@link PathString} into a {@link PathTuple}. - * The individual {@link Key}s may be empty strings. - * @typeParam PS - {@link PathString} which should be split into its - * individual {@link Key}s - * @example - * ``` - * SplitPathString<'foo'> = ['foo'] - * SplitPathString<'foo.bar.0.baz'> = ['foo', 'bar', '0', 'baz'] - * SplitPathString<'.'> = [] - * ``` - */ -export type SplitPathString = SplitPathStringImpl< - PS, - [] -> - -/** - * Type to implement {@link JoinPathTuple} tail-recursively. - * @typeParam PT - remaining {@link Key}s which needs to be joined - * @typeParam PS - accumulator of the already joined {@link Key}s - */ -type JoinPathTupleImpl< - PT extends PathTuple, - PS extends PathString -> = PT extends [infer K, ...infer R] ? JoinPathTupleImpl, `${PS}.${AsKey}`> - : PS - -/** - * Type to join a {@link PathTuple} to a {@link PathString}. - * @typeParam PT - {@link PathTuple} which should be joined. - * @example - * ``` - * JoinPathTuple<['foo']> = 'foo' - * JoinPathTuple<['foo', 'bar', '0', 'baz']> = 'foo.bar.0.baz' - * JoinPathTuple<[]> = never - * ``` - */ -export type JoinPathTuple = PT extends [ - infer K, - ...infer R -] ? JoinPathTupleImpl, AsKey> - : never - -/** - * Type which converts all keys of an object to {@link Key}s. - * @typeParam T - object type - * @example - * ``` - * MapKeys<{0: string}> = {'0': string} - * ``` - */ -type MapKeys = { [K in keyof T as ToKey]: T[K] } - -/** - * Type to access a type by a key. - * - Returns undefined if it can't be indexed by that key. - * - Returns null if the type is null. - * - Returns undefined if the type is not traversable. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * ``` - * TryAccess<{foo: string}, 'foo'> = string - * TryAccess<{foo: string}, 'bar'> = undefined - * TryAccess = null - * TryAccess = undefined - * ``` - */ -type TryAccess = K extends keyof T ? T[K] - : T extends null ? null - : undefined - -/** - * Type to access an array type by a key. - * Returns undefined if the key is non-numeric. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * ``` - * TryAccessArray = string - * TryAccessArray = undefined - * ``` - */ -type TryAccessArray< - T extends ReadonlyArray, - K extends Key -> = K extends `${ArrayKey}` ? T[number] : TryAccess - -/** - * Type to evaluate the type which the given key points to. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * @example - * ``` - * EvaluateKey<{foo: string}, 'foo'> = string - * EvaluateKey<[number, string], '1'> = string - * EvaluateKey = string - * ``` - */ -export type EvaluateKey = T extends ReadonlyArray ? IsTuple extends true ? TryAccess - : TryAccessArray - : TryAccess, K> - -/** - * Type to evaluate the type which the given path points to. - * @typeParam T - deeply nested type which is indexed by the path - * @typeParam PT - path into the deeply nested type - * @example - * ``` - * EvaluatePath<{foo: {bar: string}}, ['foo', 'bar']> = string - * EvaluatePath<[number, string], ['1']> = string - * EvaluatePath = number - * EvaluatePath = undefined - * ``` - */ -export type EvaluatePath = PT extends [ - infer K, - ...infer R -] ? EvaluatePath>, AsPathTuple> - : T - -/** - * Type which given a tuple type returns its own keys, i.e. only its indices. - * @typeParam T - tuple type - * @example - * ``` - * TupleKeys<[number, string]> = '0' | '1' - * ``` - */ -export type TupleKeys> = Exclude< - keyof T, - keyof any[] -> - -/** - * Type which extracts all numeric keys from an object. - * @typeParam T - type - * @example - * ``` - * NumericObjectKeys<{0: string, '1': string, foo: string}> = '0' | '1' - * ``` - */ -type NumericObjectKeys = ToKey< - Extract -> - -/** - * Type which extracts all numeric keys from an object, tuple, or array. - * If a union is passed, it evaluates to the overlapping numeric keys. - * @typeParam T - type - * @example - * ``` - * NumericKeys<{0: string, '1': string, foo: string}> = '0' | '1' - * NumericKeys = `${number}` - * NumericKeys<[string, number]> = '0' | '1' - * NumericKeys<{0: string, '1': string} | [number] | number[]> = '0' - * ``` - */ -export type NumericKeys = UnionToIntersection< - T extends ReadonlyArray ? IsTuple extends true ? [TupleKeys] - : [ToKey] - : [NumericObjectKeys] ->[never] - -/** - * Type which extracts all keys from an object. - * If a union is passed, it evaluates to the overlapping keys. - * @typeParam T - object type - * @example - * ``` - * ObjectKeys<{foo: string, bar: string}, string> = 'foo' | 'bar' - * ObjectKeys<{foo: string, bar: number}, string> = 'foo' - * ``` - */ -export type ObjectKeys = Exclude< - ToKey, - `${string}.${string}` | "" -> - -/** - * Type to check whether a type's property matches the constraint type - * and return its key. Converts the key to a {@link Key}. - * @typeParam T - type whose property should be checked - * @typeParam K - key of the property - * @typeParam U - constraint type - * @example - * ``` - * CheckKeyConstraint<{foo: string}, 'foo', string> = 'foo' - * CheckKeyConstraint<{foo: string}, 'foo', number> = never - * CheckKeyConstraint = `${number}` - * ``` - */ -export type CheckKeyConstraint = K extends any ? EvaluateKey extends U ? K - : never - : never - -/** - * Type which evaluates to true when the type is an array or tuple or is a union - * which contains an array or tuple. - * @typeParam T - type - * @example - * ``` - * ContainsIndexable<{foo: string}> = false - * ContainsIndexable<{foo: string} | number[]> = true - * ``` - */ -export type ContainsIndexable = IsNever< - Extract> -> extends true ? false - : true - -/** - * Type to implement {@link Keys} for non-nullable values. - * @typeParam T - non-nullable type whose property should be checked - */ -type KeysImpl = [T] extends [Traversable] ? ContainsIndexable extends true ? NumericKeys - : ObjectKeys - : never - -/** - * Type to find all properties of a type that match the constraint type - * and return their keys. - * If a union is passed, it evaluates to the overlapping keys. - * @typeParam T - type whose property should be checked - * @typeParam U - constraint type - * @example - * ``` - * Keys<{foo: string, bar: string}, string> = 'foo' | 'bar' - * Keys<{foo?: string, bar?: string}> = 'foo' | 'bar' - * Keys<{foo: string, bar: number}, string> = 'foo' - * Keys<[string, number], string> = '0' - * Keys = `${number}` - * Keys<{0: string, '1': string} | [number] | number[]> = '0' - * ``` - */ -export type Keys = IsAny extends true ? Key - : IsNever extends true ? Key - : IsNever> extends true ? never - : CheckKeyConstraint>, U> - -/** - * Type to check whether a {@link Key} is present in a type. - * If a union of {@link Key}s is passed, all {@link Key}s have to be present - * in the type. - * @typeParam T - type which is introspected - * @typeParam K - key - * @example - * ``` - * HasKey<{foo: string}, 'foo'> = true - * HasKey<{foo: string}, 'bar'> = false - * HasKey<{foo: string}, 'foo' | 'bar'> = false - * ``` - */ -export type HasKey = IsNever>> - -/** - * Type to implement {@link ValidPathPrefix} tail recursively. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @typeParam VPT - accumulates the prefix of {@link Key}s which have been - * confirmed to exist already - */ -type ValidPathPrefixImpl< - T, - PT extends PathTuple, - VPT extends PathTuple -> = PT extends [infer K, ...infer R] ? HasKey> extends true ? ValidPathPrefixImpl< - EvaluateKey>, - AsPathTuple, - AsPathTuple<[...VPT, K]> - > - : VPT - : VPT - -/** - * Type to find the longest path prefix which is still valid, - * i.e. exists within the given type. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @example - * ``` - * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'bar']> = ['foo', 'bar'] - * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'ba']> = ['foo'] - * ``` - */ -export type ValidPathPrefix = ValidPathPrefixImpl< - T, - PT, - [] -> - -/** - * Type to check whether a path through a type exists. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @example - * ``` - * HasPath<{foo: {bar: string}}, ['foo', 'bar']> = true - * HasPath<{foo: {bar: string}}, ['foo', 'ba']> = false - * ``` - */ -export type HasPath = ValidPathPrefix extends PT ? true - : false diff --git a/packages/effect-app/src/Model/filter/types/path/eager.ts b/packages/effect-app/src/Model/filter/types/path/eager.ts deleted file mode 100644 index 26e8ef271..000000000 --- a/packages/effect-app/src/Model/filter/types/path/eager.ts +++ /dev/null @@ -1,329 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { expectTypeOf } from "@effect/vitest" -import type { FieldValues } from "../fields.ts" -import type { BrowserNativeObject, Equals, IsLiteral, IsNever, Primitive } from "../utils.ts" - -import type { ArrayKey, IsTuple, TupleKeys } from "./common.ts" - -/** - * Helper type for recursively constructing paths through a type. - * `Seen` carries the set of object types already entered on this branch so - * recursion terminates on self-referential types (see {@link Path}). - */ -type PathImpl = V extends - | Primitive - | BrowserNativeObject ? `${K}` - // Stop before re-entering a type already on this branch. Recursive types - // (notably `Json`, the encoded form of `Schema.Defect()`: - // `JsonObject = { [x: string]: Json }`) would otherwise descend forever and - // blow past TS's instantiation limit => TS2589. The key itself stays a valid - // leaf path; only the unbounded descent is cut. - : [V] extends [Seen] ? `${K}` - : `${K}` | `${K}.${Path}` - -/** - * Type which eagerly collects all paths through a type - * @typeParam T - type which should be introspected - * @typeParam Seen - object types already entered on this recursion branch; - * used to terminate on self-referential types - * @example - * ``` - * Path<{foo: {bar: string}}> = 'foo' | 'foo.bar' - * ``` - */ -export type Path = T extends ReadonlyArray ? IsTuple extends true ? { - [K in TupleKeys]-?: PathImpl | "length" - }[TupleKeys] - : PathImpl | "length" - : { - [K in keyof T]-?: PathImpl - }[keyof T] - -/** - * See {@link Path} - */ -export type FieldPath = Path - -{ - // Regression: self-referential types must terminate instead of blowing past - // TS's instantiation limit (TS2589). `JsonLike` mirrors effect's `Json` — the - // encoded form of `Schema.Defect()` — whose `{ [x: string]: Json }` index - // signature previously made `Path` recurse forever. These aliases failing to - // compile (rather than resolving to a string union) signals the regression. - type JsonLike = - | string - | number - | boolean - | null - | ReadonlyArray - | { readonly [x: string]: JsonLike } - type _RawDefect = Path<{ id: string; raw: JsonLike }> - type _NestedDefect = Path<{ a: { b: { error: { raw: JsonLike } } } }> - type _ArrayOfDefect = Path<{ items: ReadonlyArray<{ raw: JsonLike }> }> - // finite paths are still produced - expectTypeOf<"id">().toExtend<_RawDefect>() - expectTypeOf<"raw">().toExtend<_RawDefect>() - expectTypeOf<"a.b.error.raw">().toExtend<_NestedDefect>() - expectTypeOf<`items.${number}.raw`>().toExtend<_ArrayOfDefect>() -} - -/** - * Type to evaluate the type which the given path points to. - * @typeParam T - deeply nested type which is indexed by the path - * @typeParam P - path into the deeply nested type - * @example - * ``` - * PathValue<{foo: {bar: string}}, 'foo.bar'> = string - * PathValue<[number, string], '1'> = string - * ``` - */ -/* dprint-ignore-start */ -export type PathValue> = - T extends any - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? PathValue - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? PathValue> - : never - : never - : P extends keyof T - ? T[P] - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? V - : never - : never - : never -/* dprint-ignore-end */ - -/** - * See {@link PathValue} - */ -export type FieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath -> = PathValue - -/* dprint-ignore-start */ -export type SetPathValue, X> = - T extends any - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? { [_ in keyof T]: _ extends K ? SetPathValue : T[_] } - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: SetPathValue, X> } - : never - : never - : P extends keyof T - ? { [_ in keyof T]: _ extends P ? X : T[_] } - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: X } - : never - : never - : never -/* dprint-ignore-end */ - -export type SetFieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath, - X -> = SetPathValue - -/* dprint-ignore-start */ -export type RefinePathValue, X extends string | number | boolean | null | bigint, Exclde extends boolean = false> = - T extends any - // recursive cases - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? { [_ in keyof T]: _ extends K ? RefinePathValue : T[_] } - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: RefinePathValue, X, Exclde> } - : never - : never - // base cases - : P extends keyof T - ? X extends T[P] - ? ({ [_ in keyof T]: _ extends P - ? Equals< - Exclde, - true, - Exclude, - IsLiteral>> - > - : T[_] - }) extends infer $T - ? Equals, true, never, $T> - : never - : Equals - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? X extends V - ? { [_ in keyof T]: Equals< - Exclde, - true, - Exclude, - IsLiteral>> - > } - : never - : never - : never - : never -/* dprint-ignore-end */ - -export type EraseNeverContainingUnionElements = T extends any ? { - [K in keyof T]: IsNever extends true ? true : false -} extends infer $NC ? true extends $NC[keyof $NC] ? never : T -: never : never - -export type RefineFieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath, - X extends string | number | boolean | null | bigint, - Exclde extends boolean = false -> = EraseNeverContainingUnionElements> - -{ - type test1 = RefineFieldPathValue<{ a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } }, "a.b", "tag1"> - expectTypeOf().toEqualTypeOf<{ a: { b: "tag1"; v1: string } }>() - - type test2 = RefineFieldPathValue<{ b: "tag1"; v1: string } | { b: "tag2"; v2: number }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: string }>() - - type test3 = RefineFieldPathValue<{ b: "tag1" | "tag2" }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1" }>() - - type test4 = RefineFieldPathValue<{ b: ("tag1" | "tag2")[] }, `b.${number}`, "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"[] }>() - - type test5 = RefineFieldPathValue<{ b: "tag1"; v1: unknown } | { b: "tag2"; v2: unknown }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: unknown }>() - - type test6 = RefineFieldPathValue< - { a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } } | { something: "else " }, - "a.b", - "tag2" - > - expectTypeOf().toEqualTypeOf<{ a: { b: "tag2"; v2: number } }>() - - type test7 = RefineFieldPathValue<{ b: string | null }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: string }>() - - type test8 = RefineFieldPathValue<{ b: string | null }, "b", null> - expectTypeOf().toEqualTypeOf<{ b: null }>() - - type test1E = RefineFieldPathValue<{ a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } }, "a.b", "tag1", true> - expectTypeOf().toEqualTypeOf<{ a: { b: "tag2"; v2: number } }>() - - type test2E = RefineFieldPathValue<{ b: "tag1"; v1: string } | { b: "tag2"; v2: number }, "b", "tag2", true> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: string }>() - - type test3E = RefineFieldPathValue<{ b: "tag1" | "tag2" | null }, "b", null, true> - expectTypeOf().toEqualTypeOf<{ b: "tag1" | "tag2" }>() - - type test4E = RefineFieldPathValue<{ b: ("tag1" | "tag2")[] }, `b.${number}`, "tag1", true> - expectTypeOf().toEqualTypeOf<{ b: "tag2"[] }>() - - type test5E = RefineFieldPathValue<{ b: "tag1"; v1: unknown } | { b: "tag2"; v2: unknown }, "b", "tag1", true> - expectTypeOf().toEqualTypeOf<{ b: "tag2"; v2: unknown }>() - - type test6E = RefineFieldPathValue< - { a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } } | { something: "else " }, - "a.b", - "tag2", - true - > - expectTypeOf().toEqualTypeOf<{ a: { b: "tag1"; v1: string } }>() - - type test10 = RefineFieldPathValue< - | { nested: { a: string; _tag: "a" } | { b: number; _tag: "b" }; _tag: "ab" } - | { nested: { b: number; _tag: "b" } | { c: boolean; _tag: "c" }; _tag: "bc" }, - "nested._tag", - "a" - > - - expectTypeOf().toEqualTypeOf<{ - nested: { - a: string - _tag: "a" - } - _tag: "ab" - }>() -} - -{ - type test1 = SetFieldPathValue<{ foo: { bar: string[] } }, `foo.bar`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean } }>() - - type test1a = SetFieldPathValue<{ foo: { bar: string[]; baz: 12 } }, `foo.bar`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean; baz: 12 } }>() - - type test2 = SetFieldPathValue<{ foo: { bar: string[] } }, `foo.bar.${number}`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean[] } }>() - - type test2a = SetFieldPathValue<{ foo: { bar: readonly string[]; baz: 3 }; ban: 123 }, `foo.bar.${number}`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: readonly boolean[]; baz: 3 }; ban: 123 }>() - - type test2b = SetFieldPathValue< - { foo: { bar: readonly { a: 1; b: 2 }[]; baz: 3 }; ban: 123 }, - `foo.bar.${number}.b`, - "b" - > - expectTypeOf().toEqualTypeOf<{ foo: { bar: readonly { a: 1; b: "b" }[]; baz: 3 }; ban: 123 }>() - - type test3 = SetFieldPathValue<{ foo: ["a", "b"] }, `foo.0`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: [boolean, "b"] }>() - - type test3a = SetFieldPathValue<{ foo: [{ a: 123 }, "b"] }, `foo.0.a`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: [{ a: boolean }, "b"] }>() -} - -/** - * Type to evaluate the type which the given paths point to. - * @typeParam TFieldValues - field values which are indexed by the paths - * @typeParam TPath - paths into the deeply nested field values - * @example - * ``` - * FieldPathValues<{foo: {bar: string}}, ['foo', 'foo.bar']> - * = [{bar: string}, string] - * ``` - */ -export type FieldPathValues< - TFieldValues extends FieldValues, - TPath extends FieldPath[] | readonly FieldPath[] -> = - & {} - & { - [K in keyof TPath]: FieldPathValue< - TFieldValues, - TPath[K] & FieldPath - > - } - -/** - * Type which eagerly collects all paths through a fieldType that matches a give type - * @typeParam TFieldValues - field values which are indexed by the paths - * @typeParam TValue - the value you want to match into each type - * @example - * ```typescript - * FieldPathByValue<{foo: {bar: number}, baz: number, bar: string}, number> - * = 'foo.bar' | 'baz' - * ``` - */ -export type FieldPathByValue = { - [Key in FieldPath]: FieldPathValue< - TFieldValues, - Key - > extends TValue ? Key - : never -}[FieldPath] diff --git a/packages/effect-app/src/Model/filter/types/path/index.ts b/packages/effect-app/src/Model/filter/types/path/index.ts deleted file mode 100644 index 67d7d71b2..000000000 --- a/packages/effect-app/src/Model/filter/types/path/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** Re-export public API */ - -export type { PathString } from "./common.ts" -export type { FieldPath, FieldPathByValue, FieldPathValue, FieldPathValues, Path, PathValue } from "./eager.ts" diff --git a/packages/effect-app/src/Model/filter/types/utils.ts b/packages/effect-app/src/Model/filter/types/utils.ts deleted file mode 100644 index 95d8aa7db..000000000 --- a/packages/effect-app/src/Model/filter/types/utils.ts +++ /dev/null @@ -1,128 +0,0 @@ -declare const $NestedValue: unique symbol - -/** - * @deprecated to be removed in the next major version - */ -export type NestedValue = { - [$NestedValue]: never -} & TValue - -/** - * @deprecated to be removed in the next major version - */ -export type UnpackNestedValue = T extends NestedValue ? U - : T extends Date | FileList | File | Blob ? T - : T extends object ? { [K in keyof T]: UnpackNestedValue } - : T - -/* -Projects that React Hook Form installed don't include the DOM library need these interfaces to compile. -React Native applications is no DOM available. The JavaScript runtime is ES6/ES2015 only. -These definitions allow such projects to compile with only --lib ES6. - -Warning: all of these interfaces are empty. -If you want type definitions for various properties, you need to add `--lib DOM` (via command line or tsconfig.json). -*/ - -export type Noop = () => void - -interface File extends Blob { - readonly lastModified: number - readonly name: string -} - -interface FileList { - readonly length: number - item(index: number): File | null - [index: number]: File -} - -export type Primitive = - | null - | undefined - | string - | number - | boolean - | symbol - | bigint - -export type BrowserNativeObject = Date | FileList | File - -export type EmptyObject = { [K in string | number]: never } - -export type NonUndefined = T extends undefined ? never : T - -export type LiteralUnion = - | T - | (U & { _?: never }) - -export type DeepPartial = T extends BrowserNativeObject | NestedValue ? T - : { [K in keyof T]?: DeepPartial } - -export type DeepPartialSkipArrayKey = T extends - | BrowserNativeObject - | NestedValue ? T - : T extends ReadonlyArray ? { [K in keyof T]: DeepPartialSkipArrayKey } - : { [K in keyof T]?: DeepPartialSkipArrayKey } - -/** - * Checks whether the type is any - * See {@link https://stackoverflow.com/a/49928360/3406963} - * @typeParam T - type which may be any - * ``` - * IsAny = true - * IsAny = false - * ``` - */ -export type IsAny = 0 extends 1 & T ? true : false - -/** - * Checks whether the type is never - * @typeParam T - type which may be never - * ``` - * IsAny = true - * IsAny = false - * ``` - */ -export type IsNever = [T] extends [never] ? true : false - -export type DeepMap = IsAny extends true ? any - : T extends BrowserNativeObject | NestedValue ? TValue - : T extends object ? { [K in keyof T]: DeepMap, TValue> } - : TValue - -export type IsFlatObject = Extract< - Exclude, - any[] | object -> extends never ? true - : false - -export type Merge = { - [K in keyof A | keyof B]?: K extends keyof A & keyof B ? [A[K], B[K]] extends [object, object] ? Merge - : A[K] | B[K] - : K extends keyof A ? A[K] - : K extends keyof B ? B[K] - : never -} - -export type Resolve = - & { - [K in keyof T]: Resolve - } - & {} - -export type ResolveFirstLevel = - & { - [K in keyof T]: T[K] - } - & {} - -export type Cast = T extends U ? T : U - -export type IsLiteral = string extends T ? False : number extends T ? False : True - -export type Extends = T extends U ? True : False - -export type IsEqual = (<_>() => _ extends T ? 1 : 2) extends (<_>() => _ extends U ? 1 : 2) ? true : false - -export type Equals = IsEqual extends true ? True : False diff --git a/packages/effect-app/src/Model/filter/types/validator.ts b/packages/effect-app/src/Model/filter/types/validator.ts deleted file mode 100644 index 062346819..000000000 --- a/packages/effect-app/src/Model/filter/types/validator.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Message } from "./errors.ts" -import type { FieldValues, InternalFieldName } from "./fields.ts" -import type { FieldPath, FieldPathValue } from "./path/index.ts" - -export type ValidationValue = boolean | number | string | RegExp - -export type ValidationRule< - TValidationValue extends ValidationValue = ValidationValue -> = TValidationValue | ValidationValueMessage - -export type ValidationValueMessage< - TValidationValue extends ValidationValue = ValidationValue -> = { - value: TValidationValue - message: Message -} - -export type ValidateResult = Message | Message[] | boolean | undefined - -export type Validate = ( - value: TFieldValue -) => ValidateResult | Promise - -export type RegisterOptions< - TFieldValues extends FieldValues = FieldValues, - TFieldName extends FieldPath = FieldPath -> = Partial<{ - required: Message | ValidationRule - min: ValidationRule - max: ValidationRule - maxLength: ValidationRule - minLength: ValidationRule - pattern: ValidationRule - validate: - | Validate> - | Record>> - valueAsNumber: boolean - valueAsDate: boolean - value: FieldPathValue - setValueAs: (value: any) => any - shouldUnregister?: boolean - onChange?: (event: any) => void - onBlur?: (event: any) => void - disabled: boolean - deps: InternalFieldName | InternalFieldName[] -}> diff --git a/packages/effect-app/src/Model/query.ts b/packages/effect-app/src/Model/query.ts deleted file mode 100644 index 22d242aca..000000000 --- a/packages/effect-app/src/Model/query.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from "./query/dsl.ts" -export * from "./query/new-kid-interpreter.ts" - -export interface RawQuery { - cosmos: (vals: { name: string }) => { - query: string - parameters: { - name: string - value: any - }[] - } - memory: (t: readonly Encoded[]) => readonly Out[] -} diff --git a/packages/effect-app/src/Model/query/dsl.ts b/packages/effect-app/src/Model/query/dsl.ts deleted file mode 100644 index b91351471..000000000 --- a/packages/effect-app/src/Model/query/dsl.ts +++ /dev/null @@ -1,3092 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Data from "effect/Data" -import { flow } from "effect/Function" -import * as Pipeable from "effect/Pipeable" -import type { Covariant } from "effect/Types" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" -import type { NonNegativeInt } from "../../Schema.ts" -import type { Ops } from "../filter/filterApi.ts" -import type { FieldValues } from "../filter/types.ts" -import type { FieldPath, FieldPathValue, RefineFieldPathValue } from "../filter/types/path/eager.ts" - -export type QAll< - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - A = TFieldValues, - R = never, - TType extends "one" | "many" = "many", - Exclusive extends boolean = false -> = - | Query - | QueryWhere - | QueryEnd - | QueryProjection - -export const QId = Symbol() -export type QId = typeof QId - -export interface QueryTogether< - out TFieldValues extends FieldValues, - out TFieldValuesRefined extends TFieldValues = TFieldValues, - out Exclusive extends boolean = false, - out T extends "initial" | "where" | "end" | "projection" = "initial", - out A = TFieldValues, - out R = never, - out TType extends "many" | "one" | "count" = "many" -> extends Pipeable.Pipeable { - readonly [QId]: { - readonly _TFieldValues: Covariant - readonly _T: Covariant - readonly _TFieldValuesRefined: Covariant - readonly _A: Covariant - readonly _R: Covariant - readonly _TT: Covariant - readonly _Exclusive: Covariant - } -} - -type ExtractTType = T extends QueryTogether ? TType : never -type ExtractExclusiveness = T extends QueryTogether - ? Exclusive - : never -type ExtractFieldValues = T extends QueryTogether ? TFieldValues - : never -type ExtractFieldValuesRefined = T extends QueryTogether - ? TFieldValuesRefined - : never -type KeysOfUnion = T extends T ? keyof T : never -/** Peel Schema `tag`/`Literal` brands down to the underlying literal value. */ -type LiteralValue = T extends { readonly literal: infer L } ? L - : T extends string | number | boolean | null | bigint ? T - // Schema.tag<"x"> / withConstructorDefault wrappers are object brands whose - // nominal shape is not a PropertyKey — fall back to never so callers treat - // them via UnwrapTag below. - : never -// Last-resort: if T is a single object brand wrapping a string literal in its -// structure, treat non-union object tags as single-literal for same-tag duals. -type TagKey = [LiteralValue] extends [never] ? ( - string extends T ? string - : [T] extends [PropertyKey] ? T - // object brand (e.g. tag<"packing">): treat as single opaque tag token - : T - ) - : LiteralValue -// One-directional: domain tag may be a single literal that is a member of a -// multi-tag projection (`"picking" extends "picking"|"picked"`). Bidirectional -// equality would reject every multi-tag flat projection. -type ExtractTagged = From extends { readonly _tag: infer FromTag } - ? [TagKey] extends [TagKey] ? From : never - : never -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never -/** - * True when `Tag` is a single tag token (string literal or opaque brand like - * Schema.tag<"packing">), not a union of tags and not bare `string`. - */ -type IsSingleLiteralTag = string extends Tag ? false - : [Tag] extends [UnionToIntersection] ? true - : false -/** - * Domain shape that may supply stored fields for projection member `I`. - * - * - True tagged unions (`A | B` with different fields per `_tag`) resolve to the - * matching member so state-owned keys are not treated as universal. - * - Flat models that only carry `_tag: "a" | "b"` on a shared shape (no - * per-tag members in the Encoded union) fall back to the full `From` so - * existing Class+Literals projections keep typechecking. - */ -type ProjectableSource = I extends { readonly _tag: infer Tag } ? ( - [ExtractTagged] extends [never] ? From : ExtractTagged - ) - : From - -/** - * One projection member is projectable when every key is either: - * - in `ExtraKeys` (computed by the query / not stored on the domain row), or - * - a key of the matching domain source member (key presence only; nested - * field types may be narrowed by the projection). - * - * Uses `keyof Source` (not `KeysOfUnion` of the whole domain union) so a field - * owned only by some tags cannot be required on every branch — except for a - * single literal tag that maps to several domain variants (same `_tag`, - * different payloads), where `KeysOfUnion` allows each variant's keys. - */ -/** - * Keys the domain may supply for projection member `I`. - * - Single-literal tagged `I` (`_tag: "packing"`): keys of *any* same-tag - * domain variant (`KeysOfUnion`) — dual packing/closed shapes stay projectable. - * - Multi-tag / string-tagged `I` (`_tag: "a"|"b"` or `string`): keys of the - * *intersection* of matched domain members (`keyof` of the source union) so - * a flat multi-tag DTO cannot claim state-only fields (e.g. `batchId` on - * `initial`). - * - Untagged `I` (plain project DTOs): keys present on *any* domain member - * (`KeysOfUnion`), matching historical `project()` behavior. - */ -type ProjectableDomainKeys = I extends { readonly _tag: infer Tag } ? ( - IsSingleLiteralTag extends true ? KeysOfUnion> - : keyof ProjectableSource - ) - : KeysOfUnion - -/** - * Keys on projection member `I` that are neither computed (`ExtraKeys`) nor - * present on the matching domain source. Key presence only — nested field - * types may be narrowed by the projection, and optional domain/projection - * keys must not fail via `{ k?: T } extends { k: T }` (the old `-?` mapped - * assignability check rejected legitimate optionalKey fields like closed.batchId). - */ -type UnprojectableKeys = { - [K in keyof I]-?: K extends ExtraKeys ? never - : K extends ProjectableDomainKeys ? never - : K -}[keyof I] - -/** - * Distribute over tagged-union projection Encoded types. A non-distributive - * check against a union only sees `keyof (A|B)` (key intersection) and misses - * branch-only fields like cancel-only omissions. - */ -type IsProjectableMember = [I] extends [FieldValues] - ? ([UnprojectableKeys] extends [never] ? true : false) - : false - -/** - * `unknown` when every member of projection Encoded `I` is projectable from - * domain Encoded `From` (plus optional ExtraKeys for computed fields); `never` - * otherwise — use as an intersection constraint on a schema argument. - */ -type ProjectableGuard = false extends ( - I extends any ? IsProjectableMember : never -) ? never - : unknown - -/** - * Compile-time proof that a projection Encoded shape only requires stored keys - * that exist on the matching domain tagged state (or non-tagged source), plus - * any `ExtraKeys` filled by `projectComputed` (counts, flags, collects, …). - * - * Catches the class of Overview.List SchemaError where a cancel/recovery state - * omits a workflow lock field (`activeRequest`) in the domain model but the - * projection still requires it on every branch. - * - * @example - * ```ts - * type _ok = ProjectableFromDomain< - * { readonly _tag: "cancelled"; readonly id: string }, - * DomainEnc - * > // unknown - * - * type _bad = ProjectableFromDomain< - * { readonly _tag: "cancelled"; readonly id: string; readonly activeRequest: null }, - * DomainEnc - * > // never — activeRequest is not on domain cancelled - * ``` - */ -export type ProjectableFromDomain< - ProjectionEncoded, - DomainEncoded, - ExtraKeys extends PropertyKey = never -> = ProjectableGuard - -export type RelationDirection = "some" | "every" -export type Relation = { relation: RelationDirection } -export type Query = QueryTogether -export type QueryWhere< - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - Exclusive extends boolean = false -> = - & QueryTogether< - TFieldValues, - TFieldValuesRefined, - Exclusive, - "where" - > - & Relation - -export type QueryEnd< - TFieldValues extends FieldValues, - TType extends "many" | "one" | "count" = "many", - Exclusive extends boolean = false -> = QueryTogether< - TFieldValues, - TFieldValues, - Exclusive, - "end", - TFieldValues, - never, - TType -> - -export type QueryProjection< - TFieldValues extends FieldValues, - A = TFieldValues, - R = never, - TType extends "many" | "one" | "count" = "many", - Exclusive extends boolean = false -> = QueryTogether< - TFieldValues, - TFieldValues, - Exclusive, - "projection", - A, - R, - TType -> - -export type ComputedProjectionOperation = (q: Query) => QueryWhere - -export type ComputedProjectionMathExpression = - | { - readonly _tag: "field" - readonly field: string - } - | { - readonly _tag: "mul" - readonly left: ComputedProjectionMathExpression - readonly right: ComputedProjectionMathExpression - } - -declare const ComputedProjectionExpressionTypeId: unique symbol - -type ComputedProjectionOutput = { - readonly [ComputedProjectionExpressionTypeId]?: Covariant -} - -export type ComputedProjectionExpression = - & ComputedProjectionOutput - & ( - | { - readonly _tag: "relation-count" - readonly path: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-any" - readonly path: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-every" - readonly path: string - readonly operation: ComputedProjectionOperation - } - | { - readonly _tag: "relation-distinct-count" - readonly path: string - readonly field: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum" - readonly path: string - readonly field: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr-by" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly unit: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr-normalized" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly unit: string - readonly toBase: string - readonly factors: Readonly> - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-collect" - readonly path: string - readonly field: string - readonly distinct: boolean - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-collect-fields" - readonly path: string - readonly fields: readonly string[] - readonly distinct: boolean - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-length" - readonly path: string - } - ) - -type NoExtraComputedKeys = string extends keyof M ? unknown - : Exclude> extends never ? unknown - : never - -/** - * An expression that aggregates values across documents (for use with {@link aggregate}). - * `agg-field` references a document field to group by; other tags are aggregate functions. - */ -export type AggregateExpression = - | { - readonly _tag: "agg-field" - readonly path: string - } - | { - readonly _tag: "agg-count" - } - | { - readonly _tag: "agg-count-when" - readonly operation: ComputedProjectionOperation - } - | { - readonly _tag: "agg-sum" - readonly field: string - } - | { - readonly _tag: "agg-min" - readonly field: string - } - | { - readonly _tag: "agg-max" - readonly field: string - } - -export type AggregateMap = Readonly> - -export type ComputedProjectionMap = Readonly> -export type Q = - | Initial - | Where - | And - | Or - | Order - | Page - | Project - | One - | Count - -export class Initial extends Data.TaggedClass("value")<{ value: "initial" }> - implements Query -{ - readonly [QId]!: any - constructor() { - super({ value: "initial" as const }) - } - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Where extends Data.TaggedClass("where")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection - subPath?: string -}> implements QueryWhere { - readonly [QId]!: any - - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class And extends Data.TaggedClass("and")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection -}> implements QueryWhere { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Or extends Data.TaggedClass("or")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection -}> implements QueryWhere { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Page extends Data.TaggedClass("page")<{ - current: Query | QueryWhere | QueryEnd - take?: number | undefined - skip?: number | undefined -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class One extends Data.TaggedClass("one")<{ - current: Query | QueryWhere | QueryEnd -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Count extends Data.TaggedClass("count")<{ - current: Query | QueryWhere | QueryEnd -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Order> - extends Data.TaggedClass("order")<{ - current: Query | QueryWhere | QueryEnd - field: TFieldName - direction: "ASC" | "DESC" - }> - implements QueryEnd -{ - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Project - extends Data.TaggedClass("project")<{ - current: Query | QueryWhere | QueryEnd - schema: S.Codec - mode: "collect" | "project" | "transform" | "aggregate" - computed?: ComputedProjectionMap - aggregateMap?: AggregateMap - }> - implements QueryProjection -{ - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export const make: () => Query = () => new Initial() - -export const where: FilterWhere = (...operation: any[]) => (current: any) => - new Where({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const and: FilterContinuationAnd = (...operation: any[]) => (current: any) => - new And({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const or: FilterContinuationOr = (...operation: any[]) => (current: any) => - new Or({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const whereEvery: WhereEveryOrSome = - ((subPath: string, ...[first, ...rest]: any[]) => (current: any) => - new Where( - { - current, - operation: typeof first === "function" - ? flow(...[first, ...rest] as [any]) - : [`${subPath}.-1.${first}`, ...rest], - relation: "every", - subPath - } as any - )) as unknown as WhereEveryOrSome -export const whereSome: WhereEveryOrSome = - ((subPath: string, ...[first, ...rest]: any[]) => (current: any) => - new Where( - { - current, - operation: typeof first === "function" - ? flow(...[first, ...rest] as [any]) - : [`${subPath}.-1.${first}`, ...rest], - relation: "some", - subPath - } as any - )) as unknown as WhereEveryOrSome - -export const order: { - < - Q extends Query | QueryWhere | QueryEnd - >( - field: FieldPath>, - direction?: "ASC" | "DESC" - ): ( - current: Q - ) => QueryEnd, "many", ExtractExclusiveness> -} = (field, direction = "ASC") => (current) => new Order({ current, field: field as any, direction }) - -export const page: { - ( - page: { skip?: number; take?: number } - ): | QueryWhere | QueryEnd>( - current: Q - ) => QueryEnd, "many", ExtractExclusiveness> -} = ({ skip, take }) => (current) => - new Page({ - current, - take, - skip - }) - -export const one: { - | QueryWhere | QueryEnd>( - current: Q - ): QueryEnd, "one", ExtractExclusiveness> -} = (current) => - new One({ - current - }) - -// it's better to implement a distinct count so that the implementation can be optimised per adapter -export const count: { - < - Q extends Query | QueryWhere | QueryEnd - >( - current: Q - ): QueryProjection, NonNegativeInt, never, "count", ExtractExclusiveness> -} = (current) => new Count({ current }) - -/** - * Attach a projection schema to a query. - * - * The `select` clause sent to the store is derived from the schema's encoded - * AST property names (top-level + per-array sub-keys), so a projection always - * narrows what is read from the store. The repository augments that select - * with `id` and `_etag` for change tracking. See {@link toFilter} and the - * dispatch in `Repository/internal/internal.ts`. - * - * Modes — pick based on shape of the decoded value and on whether the - * persistence-model (PM) reverse-mapping is needed: - * - * - `"transform"` (default when `mode` omitted): goes through the repo's - * `parseMany` pipeline. The raw row is reverse-mapped via the - * etag/PM cache (re-injecting `_etag` and any PM-shape state) before - * decoding. Decode failures `orDie` (error channel = `never`). Use when - * the schema operates on the full PM shape (e.g. full-entity reads that - * must preserve etag tracking). - * - * - `"project"`: decodes the raw encoded row directly with the supplied - * schema. No PM reverse-mapping, no etag cache merge. Decode failures - * surface as `S.SchemaError`. Use for slim DTOs / aggregations that do not - * need etag tracking and whose schema input is a plain subset of `Encoded`. - * - * - `"collect"`: like `"project"`, but the schema yields `Option` and - * `None` values are dropped post-decode (`Array.getSomes`). Use to filter - * rows during decode (e.g. discriminated-union narrowing where some rows - * should not appear in the result). - */ -export const project: { - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, I, R>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - mode: "project" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec, I, R> - & ProjectableGuard>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec - & ProjectableGuard>, - mode: "project" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec - & ProjectableGuard> - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> -} = (schema: any, mode = "transform") => (current: any) => new Project({ current, schema, mode } as any) - -/** - * Element type of an array-valued field path on `TFieldValues`. Falls back to - * `FieldValues` when the path does not resolve to an array of structs so that - * `FieldPath<...>` stays defined (it just degrades to `string`). - * - * Uses `Extract` so that when `P` defaults to the full `FieldPath` - * union, only the array-valued branches contribute to the element type. - */ -export type RelationElement< - TFieldValues extends FieldValues, - P extends FieldPath -> = Extract, ReadonlyArray> extends ReadonlyArray - ? (E extends FieldValues ? E : FieldValues) - : FieldValues - -export const relation = < - TFieldValues extends FieldValues, - const P extends FieldPath = FieldPath ->( - path: P -) => ({ - /** - * Typed math-expression builder bound to the relation's element scope: - * `relation("items").expr.field("weight")` constrains the field path to - * `FieldPath>`. - */ - expr: { - field: (field: FieldPath>): ComputedProjectionMathExpression => ({ - _tag: "field", - field - }), - mul: ( - left: ComputedProjectionMathExpression, - right: ComputedProjectionMathExpression - ): ComputedProjectionMathExpression => ({ _tag: "mul", left, right }) - }, - length: (): ComputedProjectionExpression => ({ - _tag: "relation-length", - path - }), - count: (operation?: ComputedProjectionOperation): ComputedProjectionExpression => - operation - ? { - _tag: "relation-count", - path, - operation - } - : { - _tag: "relation-count", - path - }, - any: (operation?: ComputedProjectionOperation): ComputedProjectionExpression => - operation - ? { - _tag: "relation-any", - path, - operation - } - : { - _tag: "relation-any", - path - }, - every: (operation: ComputedProjectionOperation): ComputedProjectionExpression => ({ - _tag: "relation-every", - path, - operation - }), - distinctCount: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-distinct-count", - path, - field, - operation - } - : { - _tag: "relation-distinct-count", - path, - field - }, - sum: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum", - path, - field, - operation - } - : { - _tag: "relation-sum", - path, - field - }, - sumExpr: ( - expression: ComputedProjectionMathExpression, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum-expr", - path, - expression, - operation - } - : { - _tag: "relation-sum-expr", - path, - expression - }, - sumExprBy: >>( - expression: ComputedProjectionMathExpression, - options: { unit: F }, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray<{ readonly unit: FieldPathValue, F>; readonly total: number }> - > => - operation - ? { - _tag: "relation-sum-expr-by", - path, - expression, - unit: options.unit, - operation - } - : { - _tag: "relation-sum-expr-by", - path, - expression, - unit: options.unit - }, - sumExprNormalized: ( - expression: ComputedProjectionMathExpression, - options: { - unit: FieldPath> - toBase: string - factors: Readonly> - }, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum-expr-normalized", - path, - expression, - unit: options.unit, - toBase: options.toBase, - factors: options.factors, - operation - } - : { - _tag: "relation-sum-expr-normalized", - path, - expression, - unit: options.unit, - toBase: options.toBase, - factors: options.factors - }, - collect: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression, F>>> => - operation - ? { - _tag: "relation-collect", - path, - field, - distinct: false, - operation - } - : { - _tag: "relation-collect", - path, - field, - distinct: false - }, - collectDistinct: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression, F>>> => - operation - ? { - _tag: "relation-collect", - path, - field, - distinct: true, - operation - } - : { - _tag: "relation-collect", - path, - field, - distinct: true - }, - collectFields: >[]>( - fields: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray, F[number]>> - > => - operation - ? { - _tag: "relation-collect-fields", - path, - fields, - distinct: false, - operation - } - : { - _tag: "relation-collect-fields", - path, - fields, - distinct: false - }, - collectDistinctFields: >[]>( - fields: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray, F[number]>> - > => - operation - ? { - _tag: "relation-collect-fields", - path, - fields, - distinct: true, - operation - } - : { - _tag: "relation-collect-fields", - path, - fields, - distinct: true - } -}) - -/** - * Untyped math-expression builder. Field paths are not statically validated — - * prefer the scope-bound `relation(path).expr` builder when the element type - * is known, since it constrains the field argument to `FieldPath`. - * - * The generic parameter is accepted for symmetry with the scoped builder so - * callers may opt into a tighter check via `expr.field("x")`. - */ -export const expr = { - field: ( - field: FieldPath - ): ComputedProjectionMathExpression => ({ _tag: "field", field }), - mul: ( - left: ComputedProjectionMathExpression, - right: ComputedProjectionMathExpression - ): ComputedProjectionMathExpression => ({ _tag: "mul", left, right }) -} as const - -export const computed = (value: T): T => value - -/** - * Helpers passed to the inline-builder form of {@link projectComputed}. The - * `relation` factory is bound to the source row shape so paths/fields inside - * the projection are validated against the document shape inferred from the - * pipe. - */ -export interface ComputedHelpers { - relation: >(path: P) => ReturnType> -} - -const makeComputedHelpers = (): ComputedHelpers => ({ - relation: (path) => relation(path) -}) - -/** - * Only treat computed-map keys as ExtraKeys when `M` is a concrete object type. - * `ComputedProjectionMap` is `Record`, so `keyof M` is `string` and - * would otherwise allow every projection field as "computed". - */ -type ConcreteComputedKeys = string extends keyof M ? never : Extract - -/** - * Proof that projection Encoded `I` is projectable from domain Encoded, allowing - * concrete computed keys. Intersected onto the computed-map argument so - * inference of `M` is complete before the check runs. - */ -type ProjectableComputedMap< - M extends ComputedProjectionMap, - I extends FieldValues, - Domain -> = - & M - & NoExtraComputedKeys - & ( - [ProjectableGuard>] extends [never] ? { - readonly __projectableFromDomain: - "projection fields must exist on the matching domain tagged state or be computed keys" - } - : unknown - ) - -export const projectComputed: { - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - build: (helpers: ComputedHelpers>) => ProjectableComputedMap>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection< - ExtractFieldValuesRefined, - S.Schema.Type extends Option.Option ? A : never, - never, - ExtractTType, - E - > - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - build: (helpers: ComputedHelpers>) => ProjectableComputedMap>, - mode?: "project" - ): ( - current: Q - ) => QueryProjection, S.Schema.Type, never, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - computedProjection: ProjectableComputedMap>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection< - ExtractFieldValuesRefined, - S.Schema.Type extends Option.Option ? A : never, - never, - ExtractTType, - E - > - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - computedProjection: ProjectableComputedMap>, - mode?: "project" - ): ( - current: Q - ) => QueryProjection, S.Schema.Type, never, ExtractTType, E> -} = ( - schema: any, - mapOrBuild: - | ComputedProjectionMap - | ((helpers: ComputedHelpers) => ComputedProjectionMap), - mode = "project" -) => -(current: any) => { - const computedProjection = typeof mapOrBuild === "function" - ? mapOrBuild(makeComputedHelpers()) - : mapOrBuild - return new Project({ current, schema, mode, computed: computedProjection }) -} - -/** - * Builder shape returned by {@link agg}. Field-taking methods are constrained - * to `FieldPath` so paths are validated against the document - * shape. - */ -export interface AggBuilder { - field: (path: FieldPath) => AggregateExpression - count: () => AggregateExpression - countWhen: (operation: ComputedProjectionOperation) => AggregateExpression - sum: (field: FieldPath) => AggregateExpression - min: (field: FieldPath) => AggregateExpression - max: (field: FieldPath) => AggregateExpression -} - -const makeAggBuilder = (): AggBuilder => ({ - field: (path) => ({ _tag: "agg-field", path }), - count: () => ({ _tag: "agg-count" }), - countWhen: (operation) => ({ _tag: "agg-count-when", operation }), - sum: (field) => ({ _tag: "agg-sum", field }), - min: (field) => ({ _tag: "agg-min", field }), - max: (field) => ({ _tag: "agg-max", field }) -}) - -/** - * Scope-bound aggregate-expression builder factory. Invoke with the source - * document field-value shape to get a builder whose `field`/`sum`/`min`/`max` - * arguments are constrained to `FieldPath`. - * - * Prefer the inline callback form of {@link aggregate} (`aggregate(schema, - * ($) => ({...}))`) — there the source shape is inferred from the pipe so no - * explicit type argument is needed. This factory is the escape hatch when - * the builder is constructed outside the pipe. - */ -export const agg = (): AggBuilder => - makeAggBuilder() - -/** - * Attach an aggregate projection to a query, performing GROUP BY + aggregate functions at the - * database level instead of fetching all rows and grouping in memory. - * - * Pass a builder callback to get a typed `agg` bound to the source row shape - * (inferred from the pipe — no explicit generic needed): - * - * @example - * ```ts - * repo.query( - * where("status", "active"), - * aggregate( - * S.Struct({ city: S.String, count: S.Number }), - * ($) => ({ - * city: $.field("address.city"), - * count: $.countWhen((q) => q.pipe(where("active", true))) - * }) - * ) - * ) - * ``` - * - * A plain {@link AggregateMap} is also accepted for the rare case where the - * map is built outside the pipe (loses path inference). - * - * The output is decoded directly with `schema` (no PM reverse-mapping, no etag tracking). - * Decode failures surface as `S.SchemaError`. - */ -export const aggregate: { - < - Q extends Query | QueryWhere | QueryEnd, - I extends Record, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - build: (agg: AggBuilder>) => AggregateMap - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends Record, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - aggregateMap: AggregateMap - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> -} = (schema: any, mapOrBuild: AggregateMap | ((agg: AggBuilder) => AggregateMap)) => (current: any) => { - const aggregateMap = typeof mapOrBuild === "function" - ? mapOrBuild(makeAggBuilder()) - : mapOrBuild - return new Project({ current, schema, mode: "aggregate", aggregateMap } as any) -} - -type GetArV = T extends ReadonlySet ? R - : T extends readonly (infer R)[] ? R - : never - -type GetMapK = T extends ReadonlyMap ? K : never -type GetMapV = T extends ReadonlyMap ? V : never -type GetMapEntry = T extends ReadonlyMap ? readonly [K, V] : never - -type InValues = readonly T[] | ReadonlySet - -export type FilterContinuations = { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "gt" | "gte" | "lt" | "lte" | "neq", - value: V // only numbers? - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith", - value: FieldPathValue extends string ? string : never - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "in" | "notIn", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: - | "includes" - | "notIncludes", - value: GetArV - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKey" | "notHasKey", - value: GetMapK - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasValue" | "notHasValue", - value: GetMapV - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKeyValue" | "notHasKeyValue", - value: GetMapEntry - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >(f: { - path: TFieldName - op: "eq" - value: V - }): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere -} - -export type FilterContinuationsWithSubpath = { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - value: V - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "gt" | "gte" | "lt" | "lte" | "neq", - value: V // only numbers? - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith", - value: FieldPathValue extends string ? string : never - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "in" | "notIn", - value: InValues - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: - | "includes" - | "notIncludes", - value: GetArV - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKey" | "notHasKey", - value: GetMapK - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasValue" | "notHasValue", - value: GetMapV - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKeyValue" | "notHasKeyValue", - value: GetMapEntry - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >(f: { - subPath: TFieldName - restPath: TFieldNameSub - op: "eq" - value: V - }): ( - current: Query - ) => QueryWhere -} - -/* dprint-ignore-start */ -export type RefineWithLiteral< - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V, - Exclde extends boolean = false -> = - // refine only if the value is a primitive - [V] extends [string | number | boolean | null | bigint] - ? RefineFieldPathValue - : TFieldValues -/* dprint-ignore-end */ - -export type FilteringRefinements = { - < - TFieldValues extends FieldValues, - const TFieldName extends FieldPath, - const V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral, - TFieldName extends "_tag" ? true : false // consider only _tag as an exclusive field and only in the positive case - > - < - TFieldValues extends FieldValues, - const TFieldName extends FieldPath, - const V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "neq", - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral, - E - > - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - const V extends readonly FieldPathValue[], - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "in", - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral>, - E - > -} - -export type NestedQueriesFixedRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere -} - -export type NestedQueriesFreeIntersectionRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - TFieldValuesRefined14 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere -} - -// to be safe, or forces the output to be exclusive because you can always or _tag filters -export type NestedQueriesFreeDisjointRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - TFieldValuesRefined14 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere -} - -export type FilterWhere = - & NestedQueriesFixedRefinement - & FilteringRefinements - & FilterContinuations - -export type FilterContinuationAnd = - & NestedQueriesFreeIntersectionRefinement - & FilteringRefinements - & FilterContinuations - -export type FilterContinuationOr = - & NestedQueriesFreeDisjointRefinement - & FilterContinuations - -// it does not support refinements by choice (for now) -export type WhereEveryOrSome = - & { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath - >( - subPath: TFieldName, - dude: ( - current: Query - ) => QueryWhere, - ...dudes: (( - current: QueryWhere - ) => QueryWhere)[] - ): ( - current: Query - ) => QueryWhere - } - & FilterContinuationsWithSubpath diff --git a/packages/effect-app/src/Model/query/new-kid-interpreter.ts b/packages/effect-app/src/Model/query/new-kid-interpreter.ts deleted file mode 100644 index e3c680444..000000000 --- a/packages/effect-app/src/Model/query/new-kid-interpreter.ts +++ /dev/null @@ -1,532 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { identity } from "effect/Function" -import * as Array from "../../Array.ts" -import { toNonEmptyArray } from "../../Array.ts" -import * as Option from "../../Option.ts" -import * as S from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import { dropUndefinedT } from "../../utils.ts" -import type { FilterResult } from "../filter/filterApi.ts" -import type { FieldValues } from "../filter/types.ts" -import type { FieldPath } from "../filter/types/path/eager.ts" -import { make, type Q, type QAll } from "../query/dsl.ts" - -export type AggregateIrExpression = - | { readonly _tag: "agg-count" } - | { readonly _tag: "agg-count-when"; readonly filter: readonly FilterResult[] } - | { readonly _tag: "agg-sum"; readonly field: string } - | { readonly _tag: "agg-min"; readonly field: string } - | { readonly _tag: "agg-max"; readonly field: string } - -export type AggregateIrItem = - | AggregateIrExpression - | { readonly _tag: "agg-field"; readonly path: string } - -export type ComputedProjectionMathIrExpression = - | { - readonly _tag: "field" - readonly field: string - } - | { - readonly _tag: "mul" - readonly left: ComputedProjectionMathIrExpression - readonly right: ComputedProjectionMathIrExpression - } - -export type ComputedProjectionIrExpression = - | { - readonly _tag: "relation-count" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-any" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-every" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-distinct-count" - readonly path: string - readonly field: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum" - readonly path: string - readonly field: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr-by" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly unit: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr-normalized" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly unit: string - readonly toBase: string - readonly factors: Readonly> - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-collect" - readonly path: string - readonly field: string - readonly distinct: boolean - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-collect-fields" - readonly path: string - readonly fields: readonly string[] - readonly distinct: boolean - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-length" - readonly path: string - } - -type SelectItem = - | keyof TFieldValues - | { key: string; subKeys: string[] } - | { - key: string - computed: ComputedProjectionIrExpression - } - -const isArraySelect = ( - item: SelectItem -): item is { key: string; subKeys: string[] } => typeof item === "object" && item !== null && "subKeys" in item - -const mergeArraySelect = ( - select: SelectItem[], - arraySelect: { key: string; subKeys: string[] } -) => { - const subKeys = [...new Set(arraySelect.subKeys)] - const existing = select.find( - (item): item is { key: string; subKeys: string[] } => isArraySelect(item) && item.key === arraySelect.key - ) - - if (existing) { - const existingIndex = select.indexOf(existing) - select[existingIndex] = { - key: arraySelect.key, - subKeys: [...new Set([...existing.subKeys, ...subKeys])] - } - } else { - select.push({ key: arraySelect.key, subKeys }) - } - - const scalarIndex = select.findIndex((item) => item === arraySelect.key) - if (scalarIndex >= 0) { - select.splice(scalarIndex, 1) - } -} - -type Result = { - filter: FilterResult[] - schema: S.Codec | undefined - limit: number | undefined - skip: number | undefined - order: { key: FieldPath; direction: "ASC" | "DESC" }[] - ttype: "one" | "many" | "count" | undefined - mode: "collect" | "project" | "transform" | "aggregate" | undefined - computed: Record | undefined - aggregateMap: Record | undefined -} - -const interpret = < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - A = TFieldValues, - R = never ->(_: QAll) => { - const a = _ as Q - - const data: Result = { - filter: [], - schema: undefined, - limit: undefined, - skip: undefined, - order: [], - ttype: undefined, - mode: undefined, - computed: undefined, - aggregateMap: undefined - } - - const upd = ( - v: Result - ) => { - data.filter.push(...v.filter) - data.order.push(...v.order) - if (v.limit !== undefined) data.limit = v.limit - if (v.skip !== undefined) data.skip = v.skip - if (v.ttype !== undefined) data.ttype = v.ttype - if (v.schema !== undefined) data.schema = v.schema - if (v.mode !== undefined) data.mode = v.mode - if (v.computed !== undefined) data.computed = v.computed - if (v.aggregateMap !== undefined) data.aggregateMap = v.aggregateMap - } - - const applyPath = (path: string) => (_: FilterResult): FilterResult => - _.t === "where" || _.t === "and" || _.t === "or" - ? { ..._, path: `${path}.-1.${_.path}` } - : { ..._, result: _.result.map(applyPath(path)) } - - switch (a._tag) { - case "value": - break - case "where": { - const { current, operation, relation, subPath } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { - t: "where-scope", - result: interpret(operation(make())).filter.map(subPath ? applyPath(subPath) : identity), - relation - } - ) - } else { - data.filter.push( - { - t: "where", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "and": { - const { current, operation, relation } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { t: "and-scope", result: interpret(operation(make())).filter, relation } - ) - } else { - data.filter.push( - { - t: "and", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "or": { - const { current, operation, relation } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { t: "or-scope", result: interpret(operation(make())).filter, relation } - ) - } else { - data.filter.push( - { - t: "or", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "one": { - upd(interpret(a.current)) - data.limit = 1 - data.ttype = "one" - break - } - case "count": { - upd(interpret(a.current)) - data.ttype = "count" - data.schema = S.Struct({ id: S.String }) as any - break - } - case "order": { - upd(interpret(a.current)) - data.order.push({ key: a.field, direction: a.direction }) - break - } - case "page": { - upd(interpret(a.current)) - data.limit = a.take - data.skip = a.skip - break - } - case "project": { - upd(interpret(a.current)) - if (a.mode === "aggregate" && a.aggregateMap) { - data.schema = a.schema - data.mode = "aggregate" - data.aggregateMap = Object.fromEntries( - Object.entries(a.aggregateMap).map(([key, expression]) => { - switch (expression._tag) { - case "agg-field": - return [key, { _tag: "agg-field" as const, path: expression.path }] - case "agg-count": - return [key, { _tag: "agg-count" as const }] - case "agg-count-when": { - const filter = interpret(expression.operation(make())).filter - return [key, { _tag: "agg-count-when" as const, filter }] - } - case "agg-sum": - return [key, { _tag: "agg-sum" as const, field: expression.field }] - case "agg-min": - return [key, { _tag: "agg-min" as const, field: expression.field }] - case "agg-max": - return [key, { _tag: "agg-max" as const, field: expression.field }] - } - }) - ) - break - } - if (a.computed && a.mode === "transform") { - throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'") - } - data.schema = a.schema - data.mode = a.computed - ? a.mode === "collect" ? "collect" : "project" - : a.mode - data.computed = a.computed - ? Object.fromEntries( - Object.entries(a.computed).map(([key, expression]) => { - const e = expression - const op = "operation" in e ? e.operation : undefined - const filter = op ? interpret(op(make())).filter.map(applyPath(e.path)) : [] - switch (e._tag) { - case "relation-count": - case "relation-any": - case "relation-every": - return [key, { _tag: e._tag, path: e.path, filter }] - case "relation-distinct-count": - case "relation-sum": - return [ - key, - { _tag: e._tag, path: e.path, field: e.field, filter } - ] - case "relation-sum-expr": - return [ - key, - { _tag: e._tag, path: e.path, expression: e.expression, filter } - ] - case "relation-sum-expr-by": - return [ - key, - { - _tag: e._tag, - path: e.path, - expression: e.expression, - unit: e.unit, - filter - } - ] - case "relation-sum-expr-normalized": - return [ - key, - { - _tag: e._tag, - path: e.path, - expression: e.expression, - unit: e.unit, - toBase: e.toBase, - factors: e.factors, - filter - } - ] - case "relation-collect": - return [ - key, - { - _tag: e._tag, - path: e.path, - field: e.field, - distinct: e.distinct, - filter - } - ] - case "relation-collect-fields": - return [ - key, - { - _tag: e._tag, - path: e.path, - fields: e.fields, - distinct: e.distinct, - filter - } - ] - case "relation-length": - return [key, { _tag: e._tag, path: e.path }] - } - }) - ) - : undefined - break - } - } - - return data -} - -const walkTransformation = (t: S.AST.AST): S.AST.AST => { - if (S.AST.isDeclaration(t) && t.typeParameters.length > 0) { - return walkTransformation(t.typeParameters[0]!) - } - return t -} - -const objectAsts = (ast: S.AST.AST): readonly S.AST.Objects[] => { - const t = walkTransformation(ast) - if (S.AST.isObjects(t)) { - return [t] - } - if (S.AST.isUnion(t)) { - return t.types.flatMap(objectAsts) - } - return [] -} - -const objectKeys = (ast: S.AST.AST): readonly string[] => [ - ...new Set(objectAsts(ast).flatMap((object) => object.propertySignatures.map((_) => _.name as string))) -] - -export const toFilter = < - TFieldValues extends FieldValues, - A, - R, - TFieldValuesRefined extends TFieldValues = TFieldValues ->( - q: QAll, - baseSchema?: S.Schema -) => { - // TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format - const a = interpret(q) - - // Aggregate mode: build select entirely from aggregateMap (no schema-driven field list) - if (a.mode === "aggregate" && a.aggregateMap) { - const aggSelect = Object.entries(a.aggregateMap).map(([key, item]) => { - if (item._tag === "agg-field") { - return { key, path: item.path } - } - return { key, aggregate: item } - }) - return dropUndefinedT({ - t: null as unknown as TFieldValues, - limit: a.limit, - skip: a.skip, - select: Option.getOrUndefined(toNonEmptyArray(aggSelect)) as any, - schema: a.schema, - computed: undefined, - order: Option.getOrUndefined(toNonEmptyArray(a.order)), - ttype: a.ttype, - mode: "aggregate" as const, - filter: a.filter.length ? a.filter : undefined - }) - } - - const schema = a.schema - let select: SelectItem[] = [] - // TODO: support more complex (nested) schemas? - if (schema) { - const t = walkTransformation(SchemaAST.toEncoded(schema.ast)) - const objects = [...objectAsts(t)] - if (Array.isArrayNonEmpty(objects)) { - select = [...objectKeys(t)] - for (const prop of objects.flatMap((_) => _.propertySignatures)) { - if (S.AST.isArrays(prop.type)) { - // make sure we only select when there are actually type literals in the tuple... - // otherwise we might be dealing with strings etc. - // TODO; be more strict, can't support arrays with unions that have non TypeLiteral members etc.. - const arraySelect = { - key: prop.name as string, - subKeys: Array.flatMap( - prop.type.rest, - (x) => { - return objectKeys(x) - } - ) - } - if (arraySelect.subKeys.length > 0) { - mergeArraySelect(select, arraySelect) - } - } - } - } - } - const computed = a.computed - const getSelectKey = (_: (typeof select)[number]) => { - if (typeof _ === "string") { - return _ - } - if (typeof _ === "object" && _ !== null && "key" in _) { - return _.key - } - return String(_) - } - const schemaKeys = select.map(getSelectKey) - const nonEncodedSchemaKeys = (() => { - if (!baseSchema) { - return [] as string[] - } - const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast)) - const encodedKeys = objectKeys(encoded) - return schemaKeys.filter((key) => !encodedKeys.includes(key)) - })() - const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed)) - - if (Array.isArrayNonEmpty(missingComputedKeys)) { - throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`) - } - - if (computed) { - const computedKeys = Object.keys(computed) - const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key)) - if (Array.isArrayNonEmpty(extraComputedKeys)) { - throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`) - } - select = select.filter((_) => { - const key = getSelectKey(_) - return !(key in computed) - }) - select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression }))) - } - return dropUndefinedT({ - t: null as unknown as TFieldValues, - limit: a.limit, - skip: a.skip, - select: Option.getOrUndefined(toNonEmptyArray(select)), - schema, - computed, - order: Option.getOrUndefined(toNonEmptyArray(a.order)), - ttype: a.ttype, - mode: a.mode ?? "transform", - filter: a.filter.length - ? a.filter - : undefined - }) -} diff --git a/packages/effect-app/src/NonEmptySet.ts b/packages/effect-app/src/NonEmptySet.ts deleted file mode 100644 index 1cd39e22a..000000000 --- a/packages/effect-app/src/NonEmptySet.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { NonEmptyReadonlyArray } from "effect/Array" -import type * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import type * as Order from "effect/Order" -import { flow, pipe } from "./Function.ts" -import { filter_, filterMap, filterMap_, fromArray as fromArrayOriginal, insert as insertOriginal, insert_ as insert_Original, map, map_, reduce, reduce_, remove, remove_, type Set, toArray as toArrayOriginal } from "./Set.ts" - -export interface NonEmptyBrand { - readonly NonEmpty: unique symbol -} - -export type NonEmptySet = Set & NonEmptyBrand - -function make_(ord: Order.Order, eq_?: Equivalence.Equivalence) { - const eq = eq_ - ?? ((x, y) => ord(x, y) === 0) - - const fromArray_ = fromArrayOriginal(eq) - const fromArray = flow(fromArray_, fromSet) - const fromNonEmptyArray = (arr: NonEmptyReadonlyArray) => fromArray_(arr) as NonEmptySet - const concat_ = (set: NonEmptySet, it: Iterable) => fromArray([...set, ...it]) - const insert__ = insertOriginal(eq) - const insert: (a: A) => (set: NonEmptySet) => NonEmptySet = insert__ as any - const insert_: (set: NonEmptySet, a: A) => NonEmptySet = insert_Original as any - - function replace_(set: NonEmptySet, a: A) { - return (pipe(filter_(set, (x) => !eq(x, a)), insert__(a)) as NonEmptySet) - } - - const toArray__ = toArrayOriginal(ord) - - function toArray(s: NonEmptySet) { - return toArray__(s) as NonEmptyReadonlyArray - } - - const remove__ = remove(eq) - const filterMap__ = filterMap(eq) - - return { - insert, - insert_, - remove: (a: A) => flow(remove__(a), fromSet), - remove_: flow(remove_(eq), fromSet), - reduce: reduce(ord), - reduce_: reduce_(ord), - replace: (a: A) => (set: NonEmptySet) => replace_(set, a), - replace_, - toArray, - fromArray, - fromNonEmptyArray, - from: (it: Iterable) => fromArray([...it]), - of: (a: A) => new Set([a]) as unknown as NonEmptySet, - concat_, - concat: (it: Iterable) => (set: NonEmptySet) => concat_(set, it), - - // A and B the same, useful when editing elements. - map: map(eq) as unknown as ( - f: (x: A) => A - ) => (set: NonEmptySet) => NonEmptySet, - map_: map_(eq) as unknown as ( - set: NonEmptySet, - f: (x: A) => A - ) => NonEmptySet, - filterMap: (f: (a: A) => Option.Option) => flow(filterMap__((a) => f(a)), fromSet), - filterMap_: flow(filterMap_(eq), fromSet) - } - // TODO: extend -} - -class Wrapper { - wrapped(ord: Order.Order, eq?: Equivalence.Equivalence) { - return make_(ord, eq) - } -} - -export interface NonEmptySetSchemaExtensions extends ReturnType["wrapped"]> {} - -export const make: ( - ord: Order.Order, - eq?: Equivalence.Equivalence -) => NonEmptySetSchemaExtensions = make_ - -export function fromSet(set: Set) { - if (set.size > 0) { - return Option.some(set as NonEmptySet) - } else { - return Option.none() - } -} - -// TODO -export * from "./Set.ts" diff --git a/packages/effect-app/src/Option.ts b/packages/effect-app/src/Option.ts deleted file mode 100644 index 5e0934ddc..000000000 --- a/packages/effect-app/src/Option.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -// eslint-disable-next-line import/no-unassigned-import -import "./builtin.ts" -import { getOrUndefined as value, type Some } from "effect/Option" -import * as Option from "effect/Option" - -export * from "effect/Option" - -export const getOrUndefined = value - -export function omitableToNullable(om: Option.Option | undefined) { - return om ?? Option.fromNullishOr(om) -} - -export const toBool = Option.match({ - onNone: () => false, - onSome: () => true -}) - -export const fromBool = (b: boolean) => (b ? Option.some(true) : Option.none()) - -/** - * Access property, unwrapping Options along the path - */ -export function p>>( - k: K -): (v: Option.Option) => Option.Option<_A> -export function p(k: K): (v: Option.Option) => Option.Option -export function p(k: any) { - return (v: Option.Option) => Option.flatMap(v, (a) => convert((a as any)[k])) -} -function convert(a: any) { - return Option.isSome(a) || Option.isNone(a) ? a : Option.fromNullishOr(a) -} -export type _A = A extends Some ? Y : never -type KeysMatching = { - [K in keyof T]: T[K] extends V ? K : never -}[keyof T] - -export const PartialExceptionTypeId = Symbol() -export type PartialExceptionTypeId = typeof PartialExceptionTypeId - -export class PartialException { - readonly _typeId: PartialExceptionTypeId = PartialExceptionTypeId -} - -function raisePartial(): X { - throw new PartialException() -} - -/** - * Simulates a partial function - */ -export function partial( - f: (miss: () => X) => (...args: ARGS) => A -): (...args: ARGS) => Option.Option { - return (...args) => { - try { - return Option.some(f(raisePartial)(...args)) - } catch (e) { - if (e instanceof PartialException) { - return Option.none() - } - throw e - } - } -} diff --git a/packages/effect-app/src/Pure.ts b/packages/effect-app/src/Pure.ts deleted file mode 100644 index c21bcf9db..000000000 --- a/packages/effect-app/src/Pure.ts +++ /dev/null @@ -1,450 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Chunk from "effect/Chunk" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import * as Result from "effect/Result" -import * as Context from "./Context.ts" -import { tuple } from "./Function.ts" - -const S1 = Symbol() -const S2 = Symbol() -const W = Symbol() - -export interface PureState { - readonly [S1]: (_: S) => void - readonly [S2]: () => S2 - - state: S2 -} - -export interface PureLog { - readonly [W]: () => W - log: Chunk.Chunk -} - -export interface PureEnv extends PureState, PureLog {} - -export interface PureEnvTest extends PureState, PureLog {} - -class PureEnvBase implements PureEnv { - readonly [W]!: () => W - readonly [S1]!: (_: S) => void - readonly [S2]!: () => S2 - readonly state: S2 - readonly log: Chunk.Chunk - - constructor(s: S2) { - this.state = s - this.log = Chunk.empty() - } -} - -export function makePureEnv(s: S2): PureEnv { - return new PureEnvBase(s) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function unifyPureEnv>( - self: X -): PureEnv< - [X] extends [{ [W]: () => infer W }] ? W : never, - [X] extends [{ [S1]: (_: infer S) => void }] ? S : never, - [X] extends [{ [S2]: () => infer S2 }] ? S2 : never -> { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return self -} - -export type Pure = Effect.Effect | R> - -// type dsl = ProgramDSL - -export function GMUA_( - get: Pure, - modify: (i: GA) => Pure, - update: (i: GA) => Pure -): Pure { - return Effect.flatMap(get, modify).pipe(Effect.flatMap(([s, a]) => Effect.map(update(s), () => a))) -} - -export function GMUA(modify: (i: GA) => Pure) { - return ( - get: Pure, - update: (i: GA) => Pure - ) => GMUA_(get, modify, update) -} - -export function GMU_( - get: Pure, - modify: (i: GA) => Pure, - update: (i: GA) => Pure -): Pure { - return Effect.flatMap(get, modify).pipe(Effect.flatMap(update)) -} - -export function GMU(modify: (i: GA) => Pure) { - return ( - get: Pure, - update: (i: GA) => Pure - ) => GMU_(get, modify, update) -} - -const tagg = Context.Service<{ env: PureEnv }>("PureEnv") -function castTag() { - return tagg as any as Context.Service, PureEnvEnv> -} - -export const ServiceTag = Symbol() -export type ServiceTag = typeof ServiceTag - -export abstract class PhantomTypeParameter { - protected abstract readonly [ServiceTag]: { - readonly [NameP in Identifier]: (_: InstantiatedType) => InstantiatedType - } -} - -export type ServiceShape> = Omit< - T, - keyof Context.ServiceClass.Shape -> - -export abstract class ServiceTagged extends PhantomTypeParameter {} - -export function makeService>(_: Omit) { - return _ as T -} - -export const PureEnvEnv = Symbol() -export interface PureEnvEnv extends ServiceTagged { - env: PureEnv -} - -export function get(): Pure { - return (castTag()).useSync((_) => _.env.state) -} - -export function set(s: S): Pure { - return (castTag()).useSync((_) => { - _.env.state = s - }) -} - -export type PureLogT = Pure - -export function log(w: W): PureLogT { - return (castTag()).useSync((_) => { - _.env.log = Chunk.append(_.env.log, w) - }) -} - -export function logMany(w: Iterable): PureLogT { - return (castTag()).useSync((_) => { - _.env.log = Chunk.appendAll(_.env.log, Chunk.fromIterable(w)) - }) -} - -export function runAll( - self: Effect.Effect>, - s: S4 -): Effect.Effect< - readonly [Chunk.Chunk, Result.Result], - never, - Exclude }> -> { - const a = Effect - .flatMap(self, (x) => - (castTag()) - .useSync( - ({ env: _ }) => ({ log: _.log, state: _.state }) - ) - .pipe( - Effect.map( - ({ log, state }) => tuple(log, Result.succeed(tuple(state, x))) - ) - )) - .pipe(Effect.catch((err: any) => tagg.useSync((env) => tuple(env.env.log, Result.fail(err))))) - return Effect.provide(a, Layer.succeed(tagg, { env: makePureEnv(s) as any }) as any) as any -} - -export function runResult( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runAll(self, s), ([log, r]) => tuple(log, Result.map(r, ([s]) => s))) -} - -export function runTerm( - self: Effect.Effect>, - s: S4 -) { - return Effect.flatMap( - runAll(self, s), - ([evts, r]) => Effect.map(Effect.fromResult(r), ([s3, a]) => tuple(s3, Chunk.toArray(evts), a)) - ) -} - -export function runTermDiscard( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runTerm(self, s), ([s3, w3]) => tuple(s3, w3)) -} - -export function runA( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runAll(self, s), ([log, r]) => tuple(log, Result.map(r, ([, a]) => a))) -} - -export function modify( - mod: (s: S2) => readonly [S3, A] -): Effect.Effect }> { - return (castTag()).useSync((_) => { - const [s, a] = mod(_.env.state) - _.env.state = s as any - return a - }) as any -} - -export function modifyM( - mod: (s: S2) => Effect.Effect> -): Effect.Effect> { - // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) - return Effect.flatMap( - (castTag()).useSync((_) => _), - (_: any) => - Effect.map(mod(_.env.state), ([s, a]: any) => { - _.env.state = s - return a - }) - ) as any -} - -export function updateWith(upd: (s: S2) => S3) { - return modify((_: S2) => { - const r = upd(_) - return tuple(r, r) - }) -} - -export function updateWithEffect( - upd: (s: S2, log: (evt: W) => PureLogT) => Effect.Effect> -): Effect.Effect> { - return modifyM((_: S2) => Effect.map(upd(_, log), (_) => tuple(_, _))) -} - -export type FixEnv = - | Exclude> - | PureEnvEnv - -// export function getMA(self: (s: S) => A): Pure { -// return Effect.accessM((_: PureState) => Ref.get(_.state).map(self)) -// } - -// export function getM(self: (s: S) => Pure): Pure { -// return Effect.accessM((_: PureState) => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM__(self: (s: S) => Pure) { -// return (tag: Tag>) => Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM_(tag: Tag>, self: (s: S) => Pure) { -// return Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM(tag: Tag>) { -// const access = Effect.accessServiceM(tag) -// return (self: (s: S) => Pure) => access(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function makeProgramDSL(): ProgramDSL { -// return makeDSL_() -// } - -// function makeDSL_() { -// const tag = tagg as unknown as Tag> -// const get = Effect.serviceWithEffect(tag, _ => _.env.state.get) -// function getM(self: (s: S2) => Effect.Effect) { return get.flatMap(self) } -// function get_(self: (s: S2) => A) { return get.map(self)} -// function set(s: S2) { -// return Effect.serviceWithEffect(tag, _ => _.env.state.set(s)) -// } -// function log(w: W2): Effect.Effect<{ -// env: PureEnv; -// }, never, void> { -// return Effect.serviceWithEffect(tag, _ => _.env.log.update(l => l.append(w as any))) as any -// } - -// const baseDSL = { -// get, -// log, -// set -// } - -// function modify(mod: (s: S2) => readonly [S3, A]): Effect.Effect<{ -// env: PureEnv; -// }, never, A> { -// return Effect.serviceWithEffect(tag, _ => -// _.env.state.get.map(_ => mod(_)).flatMap(([s, a]) => _.env.state.set(s as any ).map(() => a)) -// ) as any -// } - -// function modifyM(mod: (s: S2) => Effect.Effect): Effect.Effect<{ -// env: PureEnv; -// } | R, E, A> { -// // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) -// return Effect.serviceWithEffect(tag, _ => -// _.env.state.get.flatMap(_ => mod(_ as unknown as S2)).flatMap(([s, a]) => _.env.state.set(s as any).map(() => a)) -// ) as any -// } - -// function update(upd: (s: S2) => S3) { -// return modify(_ => tuple(upd(_), void 0 as void)) -// } - -// function updateM(upd: (s: S2) => Effect.Effect) { -// return modifyM(_ => upd(_).map(_ => tuple(_, void 0 as void))) -// } - -// const accessLog = Effect.serviceWithEffect(tag, _ => _.env.log.get) - -// function runAll( -// self: Effect.Effect, E, A>, -// s: S4 -// ): Effect.Effect}>, never, readonly [Chunk, Either.Either]> { -// return self.flatMap(x => -// Effect.serviceWithEffect(tag, ({ env: _ }) => -// Effect.all({ log: _.log.get, state: _.state.get }) -// // Ref.get(_.log).flatMap(log => Ref.get(_.state).map(state => ({ log, state }))) -// ).map( -// ( -// { log, state } -// ) => tuple(log, Either.right(tuple(state, x)) as Either.Either) -// ) -// ).catchAll( -// err => -// accessLog.map(log => -// tuple(log, Either.left(err) as Either.Either) -// ) -// ).provideService(tag, { env: makePureEnv(s) as any }) as any -// } - -// function runResult( -// self: Effect.Effect, E, A>, -// s: S4 -// ) { -// return runAll(self, s).map(([log, r]) => tuple(log, r.map(([s]) => s))) -// } - -// function runA( -// self: Effect.Effect, E, A>, -// s: S4 -// ) { -// return runAll(self, s).map(([log, r]) => tuple(log, r.map(([, a]) => a))) -// } - -// return { -// ...baseDSL, - -// getM, -// get_, -// accessLog, -// runAll, -// runA, -// runResult, - -// modify, -// modifyM, -// update, -// updateM -// } -// } - -// type dsl_ = ReturnType> - -// export interface ProgramDSL extends dsl_ { } - -// export function dslmodifyM(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => Effect.Effect) { -// return dsl.modifyM(_ => mod(_, dsl)) -// } - -// export function dslmodify(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => readonly [S, A]) { -// return dsl.modify(_ => mod(_, dsl)) -// } - -// export function dslupdateM(dsl: ProgramDSL, upd: (s: S2, dsl: ProgramDSL) => Effect.Effect) { -// return dsl.updateM(_ => upd(_, dsl)) -// } - -// export function dslupdate(dsl: ProgramDSL, upd: (s: S, dsl: ProgramDSL) => S) { -// return dsl.update(_ => upd(_, dsl)) -// } - -// export interface ZPure {} - -// export class ZPureImpl implements ZPure { -// zipRight( -// f: ZPure -// ): ZPure { -// throw new Error("not implemented") -// } - -// flatMap( -// f: (a: A) => ZPure -// ): ZPure { -// throw new Error("not implemented") - -// } -// } - -// declare type Eff1 = Effect.Effect, never, void> - -// function flatMap(self: Effect.Effect, E, A>, (s: S) => Effect.Effect) { -// } - -// export interface PureEnv2 extends PureState2, PureLog {} - -// const S1 = Symbol() -// const S2 = Symbol() -// const W = Symbol() -// export interface PureState2 { -// readonly [S1]: () => S -// readonly [S2]: () => S2 - -// readonly state: Ref -// } - -// const abc = Do($ => { -// const s = $(Pure.get<{ a: 1 }>()) -// console.log(s) -// // const a = $(Pure.log("hello")) -// $(Pure.log("hallo" as const)) -// $(Pure.log("hello" as const)) -// $(Pure.log<"hallo" | "hello" | 5>(5)) -// }) - -// declare const a: Effect.Effect< -// | { -// env: PureEnv -// } -// | { env: PureEnv } -// | { env: PureEnv }, -// never, -// never -// > -// type R = _R -// type Env = T extends { env: infer rr } ? rr : never - -// type RRRR = Env - -// const abcc = unifyPureEnv( -// undefined as any as RRRR -// ) -// const test = Pure.runAll( -// abc, -// { a: 1 } -// ) diff --git a/packages/effect-app/src/QueueMaker.ts b/packages/effect-app/src/QueueMaker.ts deleted file mode 100644 index bfcf6213e..000000000 --- a/packages/effect-app/src/QueueMaker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestContext } from "./RequestContext.ts" - -/** - * Per-message context envelope (tenant/store id, request metadata) carried - * alongside message-schema payloads. Alias of {@link RequestContext}. - * - * All that remains of the former queue machinery (SB/SQLite/mem `QueueMaker` - * implementations and the `QueueBase` interface) after the move to cluster - * entities — the message schemas that embed it still reference this envelope. - */ -export const QueueMeta = RequestContext diff --git a/packages/effect-app/src/RequestContext.ts b/packages/effect-app/src/RequestContext.ts deleted file mode 100644 index b8c6a7b2b..000000000 --- a/packages/effect-app/src/RequestContext.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as Context from "./Context.ts" -import { UserProfileId } from "./ids.ts" -import * as S from "./Schema.ts" -import { NonEmptyString255 } from "./Schema.ts" - -export const Locale = S.Literals(["en", "de"]) -export type Locale = typeof Locale.Type - -export class LocaleRef extends Context.Reference("Locale", { defaultValue: (): Locale => "en" }) {} - -export class RequestContext extends S.Opaque()(S.Struct({ - span: S.Struct({ - traceId: S.String, - spanId: S.String, - sampled: S.Boolean - }), - name: NonEmptyString255, - locale: Locale, - sourceId: S.optional(NonEmptyString255), // TODO? - namespace: NonEmptyString255, - /** @deprecated */ - userProfile: S.optional(S.Struct({ sub: UserProfileId })) // -})) { - static toMonitoring(this: void, self: RequestContext) { - return { - operationName: self.name, - locale: self.locale - } - } -} - -export const spanAttributes = (ctx: Pick & Partial) => ({ - "code.function.name": ctx.name, - "app.locale": ctx.locale, - "app.tenant.id": ctx.namespace, - ...ctx.sourceId ? { "client.id": ctx.sourceId } : {}, - ...(ctx.userProfile?.sub - ? { - "user.id": ctx - .userProfile - .sub, - "user.roles": "roles" in ctx - .userProfile - ? ctx.userProfile.roles - : undefined - } - : {}) -}) diff --git a/packages/effect-app/src/RequestScopedDependencies.ts b/packages/effect-app/src/RequestScopedDependencies.ts deleted file mode 100644 index e045f7a56..000000000 --- a/packages/effect-app/src/RequestScopedDependencies.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as Data from "effect/Data" -import type * as Scope from "effect/Scope" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" - -export type Root = "root" - -export class RequestScopedDependencyNotStartedError extends Data.TaggedError( - "RequestScopedDependencyNotStartedError" -)<{ readonly key: string }> {} - -export interface RequestScopedDependency extends Context.Reference { - readonly current: Effect.Effect - readonly layer: Layer.Layer -} - -export const access = ( - dependency: Context.Reference, - key: string -): Effect.Effect => - dependency.pipe( - Effect.filterOrFail((_): _ is Service => _ !== "root", () => new RequestScopedDependencyNotStartedError({ key })) - ) - -export const make = ( - key: string, - service: Effect.Effect -): RequestScopedDependency> => { - const dependency = Context.Reference(key, { defaultValue: () => "root" }) - return Object.assign(dependency, { - current: access(dependency, key), - layer: Layer.effect(dependency, service) - }) -} - -export const layer = < - Deps extends readonly [RequestScopedDependency, ...Array>] ->( - ...dependencies: Deps -): Layer.Layer< - never, - { [K in keyof Deps]: Deps[K] extends RequestScopedDependency ? E : never }[number], - { [K in keyof Deps]: Deps[K] extends RequestScopedDependency ? R : never }[number] -> => dependencies.map((_) => _.layer).reduce((acc, l) => Layer.merge(acc, l)) as any diff --git a/packages/effect-app/src/Schema.ts b/packages/effect-app/src/Schema.ts deleted file mode 100644 index 606f9cdd2..000000000 --- a/packages/effect-app/src/Schema.ts +++ /dev/null @@ -1,499 +0,0 @@ -import * as S from "effect/Schema" -import * as EffectSchemaParser from "effect/SchemaParser" -import { type Simplify } from "effect/Struct" -import type * as Tracer from "effect/Tracer" -import type { RequiredKeys } from "effect/Types" -import type { Arbitrary as FastCheckArbitrary } from "fast-check" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import { Email as EmailT, type Email as EmailType } from "./Schema/email.ts" -import { concurrencyUnbounded, withDefaultMake, withDefaultParseOptions } from "./Schema/ext.ts" -import type { FC } from "./Schema/FastCheck.ts" -import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.ts" -import { type AST } from "./Schema/schema.ts" -import * as SchemaAST from "./SchemaAST.ts" -import { copy, defineOwn, extendM, type StructuralCopyOrigin } from "./utils.ts" - -// --------------------------------------------------------------------------- -// Default helpers — re-exported from effect/Schema -// -// The five helpers below are surfaced explicitly so the (important) policy -// around them lives next to the export. See also the file-level note in -// `./Schema/ext.ts` and the documented wrappers in -// `./Schema/ext.ts`, `./Schema/numbers.ts`, `./Schema/moreStrings.ts`, -// and `./ids.ts`. -// -// **Construction-only**: `withConstructorDefault` fills the field when it -// is omitted from `.make(...)` input. It is NOT applied during decode, so -// it CANNOT be used to just-in-time migrate database fields. A stored -// record missing the field will still fail to decode. -// -// **`withDecodingDefault*` is discouraged**: a missing field in persisted -// data is just as likely to be data corruption as it is an old-shape -// document; silently substituting a default hides the problem and can -// poison downstream aggregates. Prefer an explicit, preferably versioned -// migration of database data over shoving missing fields under the rug. -// --------------------------------------------------------------------------- - -/** - * Attach a default value used **only** when constructing a value (e.g. via - * `.make(...)` or struct constructors) and the field is omitted from input. - * - * **Not applied during decode.** Decoding a payload that is missing the - * field will still raise a parse error. Do **not** rely on this to migrate - * database fields just-in-time — see the section header above. - * - * @see {@link withDecodingDefault} / {@link withDecodingDefaultType} — - * decode-time variants (discouraged for persisted data; use explicit, - * versioned migrations instead). - */ -export { withConstructorDefault } from "effect/Schema" - -/** - * Attach a default value used during decode when the field's `Encoded` value - * is missing **or** `undefined`. The default is specified as an `Encoded` - * value and threaded through the schema's decode step. - * - * **Discouraged for persisted data.** A missing field in a stored record is - * just as likely to be data corruption as it is an old-shape document; - * silently substituting a default hides the problem. Prefer an explicit, - * preferably versioned migration of database data — see the section header - * above. - * - * @see {@link withDecodingDefaultKey} — key-absent-only variant - * @see {@link withDecodingDefaultType} — `Type`-side variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefault } from "effect/Schema" - -/** - * Attach a default value used during decode when the field **key is absent** - * (note: not when present and `undefined`). The default is an `Encoded` - * value. - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefault} — value-absent-or-undefined variant - * @see {@link withDecodingDefaultTypeKey} — `Type`-side variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultKey } from "effect/Schema" - -/** - * Attach a default value used during decode when the field is missing **or** - * `undefined`. The default is specified as a `Type` value (i.e. on the - * decoded side). - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefault} — `Encoded`-side variant - * @see {@link withDecodingDefaultTypeKey} — key-absent-only variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultType } from "effect/Schema" - -/** - * Attach a default value used during decode when the field **key is absent** - * (note: not when present and `undefined`). The default is a `Type` value. - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefaultKey} — `Encoded`-side variant - * @see {@link withDecodingDefaultType} — value-absent-or-undefined variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultTypeKey } from "effect/Schema" - -export * from "effect/Schema" - -export * from "./Schema/Class.ts" -export { Class, ErrorClass, Opaque, OpaqueErrorFacadeClass, OpaqueFacade, OpaqueShape, OpaqueType, TaggedClass, TaggedErrorClass } from "./Schema/Class.ts" - -export { fromBrand, nominal } from "./Schema/brand.ts" -export { Array, Boolean, Date, DateFromString, DateValid, Finite, Literals, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.ts" -export { Int, NonNegativeInt } from "./Schema/numbers.ts" - -export * from "./Schema/email.ts" -export * from "./Schema/ext.ts" -export * from "./Schema/moreStrings.ts" -export * from "./Schema/numbers.ts" -export * from "./Schema/phoneNumber.ts" -export * from "./Schema/schema.ts" -export * from "./Schema/SpecialJsonSchema.ts" -export * from "./Schema/SpecialOpenApi.ts" -export * from "./Schema/strings.ts" -export { NonEmptyString } from "./Schema/strings.ts" - -export * as SchemaIssue from "effect/SchemaIssue" - -/** Fast-check generator factory previously exported as `Schema.Arbitrary`. */ -export type Arbitrary = (fc: FC) => FastCheckArbitrary - -export const decodeEffectConcurrently: typeof S.decodeEffect = withDefaultParseOptions(S.decodeEffect) -export const decodeUnknownEffectConcurrently: typeof S.decodeUnknownEffect = withDefaultParseOptions( - S.decodeUnknownEffect -) -export * as SchemaParser from "./Schema/SchemaParser.ts" - -/** - * Schema for a TypeScript `void` return value — the canonical `Void` used - * across the app, including RPC success schemas. Accepts **any present value** - * at runtime and discards it to `undefined`, while keeping the decoded/encoded - * type as `void`. Use `S.Undefined` when the input must be exactly `undefined`. - */ -export interface Void extends S.Void {} -export const Void: Void = S.Void - -// --------------------------------------------------------------------------- -// Struct / NonEmptyArray / Record -// --------------------------------------------------------------------------- - -export function Struct( - fields: Fields -): Struct { - const result = S.Struct(fields).annotate(concurrencyUnbounded) - const allowVoidMake = (schema: any): any => { - // Normalize omitted input to an empty object so optional/default-only structs can be constructed with make(). - // Do not read schema.make first: Effect 4 caches it as a non-writable own property. - const origMake = EffectSchemaParser.make(schema) - const origMakeOption = EffectSchemaParser.makeOption(schema) - const origMakeEffect = EffectSchemaParser.makeEffect(schema) - const wrap = (orig: (input: any, options?: any) => any) => - function(this: any, input: any, options?: any) { - return orig.call(this, input === undefined ? {} : input, options) - } - defineOwn(schema, "make", wrap(origMake)) - defineOwn(schema, "makeOption", wrap(origMakeOption)) - defineOwn(schema, "makeEffect", wrap(origMakeEffect)) - return schema - } - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origMapFields: any = result.mapFields - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origAnnotate: any = result.annotate - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origAnnotateKey: any = result.annotateKey - - const preserveCopyAndMethods = (schema: any): any => { - schema.copy = copy - schema.mapFields = function(this: any, f: any, options?: any) { - return (result as any).mapFields.call(this, f, options) - } - schema.annotate = function(this: any, annotations?: any) { - return (result as any).annotate.call(this, annotations) - } - schema.annotateKey = function(this: any, annotations?: any) { - return (result as any).annotateKey.call(this, annotations) - } - return allowVoidMake(schema) - } - ;(result as any).mapFields = function(this: any, f: any, options?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const mapped = origMapFields.call(this, f, options).annotate(concurrencyUnbounded) - return preserveCopyAndMethods(mapped) - } - ;(result as any).annotate = function(this: any, annotations?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const annotated = origAnnotate.call(this, annotations) - return preserveCopyAndMethods(annotated) - } - ;(result as any).annotateKey = function(this: any, annotations?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const annotated = origAnnotateKey.call(this, annotations) - return preserveCopyAndMethods(annotated) - } - ;(result as any).copy = copy - allowVoidMake(result) - return result as Struct -} - -export interface Struct extends - S.Bottom< - Struct.Type, - Struct.Encoded, - Struct.DecodingServices, - Struct.EncodingServices, - AST.Objects, - // Rebuild is what's returned from annotate etc - Struct, - Struct.MakeIn, - Struct.Iso - > -{ - /** - * The field definitions of this struct. Spread them into a new struct to - * reuse fields across schemas. - * - * **Example** (Reusing fields across structs) - * - * ```ts - * import * as Schema from "effect/Schema" - * - * const Timestamped = Schema.Struct({ - * createdAt: Schema.Date, - * updatedAt: Schema.Date - * }) - * - * const User = Schema.Struct({ - * ...Timestamped.fields, - * name: Schema.String, - * email: Schema.String - * }) - * ``` - */ - readonly fields: Fields - /** - * Returns a new struct with the fields modified by the provided function. - * - * **Options** - * - * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints - * that were attached to the original union. Defaults to `false`. - * - * **Warning**: This is an unsafe operation. Since `mapFields` - * transformations change the schema type, the original refinement functions - * may no longer be valid or safe to apply to the transformed schema. Only - * use this option if you have verified that your refinements remain correct - * after the transformation. - */ - mapFields( - f: (fields: Fields) => To, - options?: { - readonly unsafePreserveChecks?: boolean | undefined - } | undefined - ): Struct>> - - // added copy - readonly copy: StructuralCopyOrigin> -} - -export declare namespace Struct { - export type Fields = S.Struct.Fields - export type Type = S.Struct.Type - export type Encoded = S.Struct.Encoded - export type DecodingServices = S.Struct.DecodingServices - export type EncodingServices = S.Struct.EncodingServices - // changed; all optional allows void - export type MakeIn = RequiredKeys> extends never - ? void | S.Struct.MakeIn - : S.Struct.MakeIn - export type Iso = S.Struct.Iso -} - -export type StructNestedEncodedError = { - readonly _tag: "StructNestedEncodedError" - readonly message: "Expected a Struct schema or a schema with from.Encoded" - readonly schema: T -} - -export type StructNestedEncoded = T extends { fields: infer Fields extends S.Struct.Fields } ? Struct.Encoded - : T extends { readonly from: { readonly Encoded: infer Encoded } } ? Encoded - : StructNestedEncodedError - -export function NonEmptyArray(value: Value): S.NonEmptyArray { - return S.NonEmptyArray(value).annotate(concurrencyUnbounded) -} - -export function TaggedStruct( - value: Tag, - fields: Fields -): TaggedStruct { - return Struct({ _tag: S.tag(value), ...fields }) as any -} -export interface TaggedStruct - extends Struct<{ readonly _tag: S.tag } & Fields> -{} -export declare namespace TaggedStruct { - export type Fields = S.Struct.Fields - export type Type = S.Struct.Type< - { readonly _tag: S.tag } & F - > - export type Encoded = S.Struct.Encoded< - { readonly _tag: S.tag } & F - > - export type DecodingServices = - S.Struct.DecodingServices< - { readonly _tag: S.tag } & F - > - export type EncodingServices = - S.Struct.EncodingServices< - { readonly _tag: S.tag } & F - > - export type MakeIn = S.Struct.MakeIn< - { readonly _tag: S.tag } & F - > - export type Iso = S.Struct.Iso< - { readonly _tag: S.tag } & F - > -} - -export function Record( - key: Key, - value: Value -): S.$Record { - return S.Record(key, value).annotate(concurrencyUnbounded) -} -export declare namespace Record { - export type Key = S.Record.Key - export type Type = S.Record.Type - export type Encoded = S.Record.Encoded -} - -export const SpanId = Symbol() -export type SpanId = typeof SpanId - -export interface WithOptionalSpan { - [SpanId]?: Tracer.Span -} - -export const Email = EmailT.pipe(withDefaultMake) - -export type Email = EmailType - -export const PhoneNumber = PhoneNumberT.pipe(withDefaultMake) - -export type PhoneNumber = PhoneNumberType - -// Copied from SchemaAST.collectSentinels (marked @internal in effect). -// Returns all { key, literal } pairs that can discriminate a union member. -const getTagFromAST = (schema: S.Top): string => { - const sentinels = collectSentinelsFromAST(schema.ast) - const sentinel = sentinels.find((s) => s.key === "_tag") - if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal - throw new Error("No _tag literal found on schema member") -} - -function collectSentinelsFromAST( - ast: SchemaAST.AST -): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> { - switch (ast._tag) { - case "Declaration": { - const s = ast.annotations?.["~sentinels"] - return Array.isArray(s) ? s : [] - } - case "Objects": - return ast.propertySignatures.flatMap( - (ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => { - const type = ps.type - if (!SchemaAST.isOptional(type)) { - if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }] - if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }] - } - return [] - } - ) - case "Suspend": - return collectSentinelsFromAST(ast.thunk()) - default: - return [] - } -} - -export const tags = < - Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })> ->( - self: Members -) => - S.Literals( - self.map(getTagFromAST) as { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } - ) as S.Literals< - { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } - > - -type TaggedUnionMembers = NonEmptyReadonlyArray< - S.Top & { readonly Type: { readonly _tag: string } } -> - -type TaggedUnionTags = S.Literals< - { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } -> - -type TaggedPropertyKeys = { - [K in keyof A & string]: A[K] extends Members[number]["Type"] ? K : never -}[keyof A & string] - -type PropertyGuardsFor< - Members extends TaggedUnionMembers, - K extends string, - A -> = - & { - readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: ( - target: A - ) => target is A & { readonly [P in K]: M["Type"] } - } - & { - readonly isAnyOf: >( - tags: Tags - ) => ( - target: A - ) => target is A & { readonly [P in K]: Extract } - } - -type PropertyGuards< - Members extends TaggedUnionMembers, - K extends string -> = - & { - readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: < - T extends { readonly [P in K]: Members[number]["Type"] } - >(target: T) => target is T & { readonly [P in K]: M["Type"] } - } - & { - readonly isAnyOf: >( - tags: Tags - ) => ( - target: T - ) => target is T & { readonly [P in K]: Extract } - } - -type TaggedUnionWithTags = S.toTaggedUnion<"_tag", Members> & { - readonly tags: TaggedUnionTags - readonly generateGuards: (property: K) => PropertyGuards - readonly generateGuardsFor: () => >( - property: K - ) => PropertyGuardsFor -} - -const extendTaggedUnionWithTags = ( - schema: S.Union -): TaggedUnionWithTags => - extendM(schema.pipe(S.toTaggedUnion("_tag")), (tagged) => { - const makeGuards = (property: string) => { - const result: any = {} - const guards: Record boolean> = tagged.guards - for (const tag of Object.keys(guards)) { - const guard = guards[tag]! - result[`is${tag}`] = (target: any) => guard(target[property]) - } - result.isAnyOf = (memberTags: Array) => { - const check = tagged.isAnyOf(memberTags) - return (target: any) => check(target[property]) - } - return result - } - return { - tags: tags(schema.members), - generateGuards: makeGuards, - generateGuardsFor: () => makeGuards - } - }) - -export const ExtendTaggedUnion = ( - schema: S.Union -): TaggedUnionWithTags => extendTaggedUnionWithTags(schema) - -export const TaggedUnion = < - Members extends TaggedUnionMembers ->(members: Members): TaggedUnionWithTags => extendTaggedUnionWithTags(S.Union(members)) diff --git a/packages/effect-app/src/Schema/Class.ts b/packages/effect-app/src/Schema/Class.ts deleted file mode 100644 index a13163e22..000000000 --- a/packages/effect-app/src/Schema/Class.ts +++ /dev/null @@ -1,683 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as S from "effect/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import * as SchemaIssue from "effect/SchemaIssue" -import * as SchemaTransformation from "effect/SchemaTransformation" -import type { Struct as EffectAppStruct } from "../Schema.ts" -import * as SchemaAST from "../SchemaAST.ts" -import { copyOrigin } from "../utils.ts" -import { concurrencyUnbounded } from "./ext.ts" -import * as SchemaParser from "./SchemaParser.ts" - -type ClassAnnotations = S.Annotations.Declaration - -export interface EnhancedClass< - Self, - SchemaS extends S.Constraint & { readonly fields: S.Struct.Fields }, - Inherited -> extends S.Class { - /** - * See `copyOrigin` docs in `utils.ts` for return-type design details. - */ - readonly copy: ReturnType Self>> -} -type MissingSelfGeneric = - `Missing \`Self\` generic - use \`class Self extends ${Usage}()(${Params}{ ... })\`` - -type HasFields = { - readonly fields: Fields -} | { - readonly from: HasFields -} - -type ClassOptions = { - readonly strict?: boolean -} - -export declare const ExtendedSchemaNoEncoded: unique symbol - -export type ExtendedSchemaNoEncoded = typeof ExtendedSchemaNoEncoded - -/** - * Override schema `Encoded` while keeping `Constraint` (intersection, not `Omit`). - * Free type-params for `Encoded` keep the conditional unresolved — callers that - * need Encoded overrides use the intersection form of the class factory return type. - */ -type WithEncoded = SchemaS & { readonly Encoded: Encoded } - -type ExtendedSchema = [ - Encoded -] extends [ExtendedSchemaNoEncoded] ? SchemaS - : WithEncoded - -/** Struct fields schema, optionally with a caller-supplied Encoded override. */ -type ClassFieldsSchema = ExtendedSchema, Encoded> - -export type Class = EnhancedClass< - Self, - S, - Inherited -> - -/** - * Build a modified Declaration that accepts struct-matching values during - * encoding, given the original Declaration and the class's fields. - */ -function makeRelaxedDeclaration( - ast: SchemaAST.Declaration, - fields: S.Struct.Fields, - cls: any -): SchemaAST.Declaration { - const parseOptions = ast.annotations?.["parseOptions"] as SchemaAST.ParseOptions | undefined - const structSchema = S.Struct(fields) - const annotatedStruct = parseOptions ? S.toType(structSchema).annotate({ parseOptions }) : S.toType(structSchema) - const decodeStruct = SchemaParser.decodeUnknownEffect(annotatedStruct) - - return new SchemaAST.Declaration( - ast.typeParameters, - () => (input: unknown, self: SchemaAST.Declaration, options: SchemaAST.ParseOptions) => { - if (input instanceof cls) { - return Effect.succeed(input) - } - if (input !== null && typeof input === "object") { - return decodeStruct(input, options) - } - return Effect.fail(new SchemaIssue.InvalidType(self, input)) - }, - ast.annotations, - ast.checks, - ast.encoding, - ast.context - ) -} - -// --------------------------------------------------------------------------- -// Class — like Schema.Class but with relaxed encoding -// --------------------------------------------------------------------------- - -/** - * Like `Schema.Class`, but the resulting class accepts plain objects matching - * the struct schema during encoding — not only `instanceof` or type-id - * checks. - * - * @example - * ```ts - * import * as Schema from "effect/Schema" - * import { Class } from "./Class.ts" - * - * class A extends Class("A")({ a: Schema.String }) {} - * - * // Construction works as normal: - * new A({ a: "hello" }) - * - * // Encoding accepts plain objects: - * Schema.encodeUnknownSync(A)({ a: "hello" }) // { a: "hello" } - * ``` - */ -export const Class: ( - identifier: string -) => ( - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"Class"> - : EnhancedClass< - Self, - ClassFieldsSchema, - Brand - > = (identifier) => (fields, annotations, options) => { - const relaxed = options?.strict === false - // Build the original Schema.Class - const Base = (S.Class as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations }) - // Get the original ast getter from the base class - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - - // Cache per-class to avoid recomputing - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - // Call the original getter with `this` bound to the actual user class, - // so getClassSchema(this) creates a schema that uses `new this(...)`. - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// TaggedClass — like Schema.TaggedClass but with relaxed encoding -// --------------------------------------------------------------------------- - -/** - * Like `Schema.TaggedClass`, but the resulting class accepts plain objects - * matching the struct schema during encoding. - * - * @example - * ```ts - * import * as Schema from "effect/Schema" - * import { TaggedClass } from "./Class.ts" - * - * class Circle extends TaggedClass()("Circle", { - * radius: Schema.Number - * }) {} - * - * Schema.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 }) - * ``` - */ -export const TaggedClass: ( - identifier?: string -) => ( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"TaggedClass"> - : EnhancedClass< - Self, - ClassFieldsSchema<{ readonly _tag: S.tag } & Fields, Encoded>, - Brand - > = (identifier) => (tag, fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.TaggedClass as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// ErrorClass — like Schema.ErrorClass but with relaxed encoding -// --------------------------------------------------------------------------- - -export const ErrorClass: ( - identifier: string -) => ( - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"ErrorClass"> - : EnhancedClass< - Self, - ClassFieldsSchema, - Cause.YieldableError & Brand - > = (identifier) => (fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.Error as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// TaggedErrorClass — like Schema.TaggedErrorClass but with relaxed encoding -// --------------------------------------------------------------------------- - -export const TaggedErrorClass: ( - identifier?: string -) => ( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"TaggedErrorClass"> - : EnhancedClass< - Self, - ClassFieldsSchema<{ readonly _tag: S.tag } & Fields, Encoded>, - Cause.YieldableError & Brand - > = (identifier) => (tag, fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.TaggedError as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -export interface Opaque extends S.Opaque { - readonly "Encoded": Encoded extends ExtendedSchemaNoEncoded ? SchemaS["Encoded"] : Encoded -} - -export const Opaque: () => ( - schema: S -) => Opaque & Omit = S.Opaque as any - -/** - * Like {@link Opaque}, but the class **instance type is exactly `Self`** (the supplied - * decoded `Type`) instead of the structurally-computed `struct["Type"] & Brand`. - * - * Stock `Opaque` types the instance as `struct["Type"] & Brand` and only overrides the - * schema's `Type`/`Encoded` *members* with `Self`/`Encoded`. So `make`/`copy`/consumers - * still resolve the struct's mapped `Type`. With a codegen-supplied pre-expanded literal - * `Type` interface, `OpaqueType` lets all of those resolve a single **named** interface - * (resolved once per checker) instead of re-deriving the mapped `Type` — cutting - * instantiation on `Type`-touching consumers. - * - * Use with `class X extends OpaqueType()(struct) {}` where `X.Type` - * and `X.Encoded` are generated literal interfaces (see `@effect-app/eslint-codegen-model`, - * `static` + `type` mode). - * - * KNOWN GAP — **no branding**: the instance type is a plain structural `Self`, so opaque - * types of identical shape are mutually assignable. (Stock `Opaque` is also structural by - * default — `Brand` defaults to `{}` — so this only differs if you passed a non-default - * `Brand`.) Re-introducing a nominal brand on top of a supplied `Self` (e.g. branding the - * generated `Type` interface) is not yet implemented. - * - * NOTE: only `Type` (via `Self`) and `Encoded` are supplied statically here; `make`'s input - * (`~type.make.in`) and other derived members are still computed from the struct. See - * a future `OpaqueShape`-style helper if those also need to be supplied. - */ -export interface OpaqueType extends S.Opaque { - readonly "Encoded": Encoded extends ExtendedSchemaNoEncoded ? SchemaS["Encoded"] : Encoded - new(_: never): Self -} - -export const OpaqueType: () => ( - schema: S -) => OpaqueType & Omit = S.Opaque as any - -type OpaqueFacadeConstructorArgs = {} extends MakeIn ? [props?: MakeIn, options?: S.MakeOptions] - : [props: MakeIn, options?: S.MakeOptions] - -// Only the codec channels are required. `copy`/`fields`/`mapFields` are NOT -// required here: transformed schemas (`.pipe(S.encodeKeys/annotate/filter/...)`) -// don't expose them at the static level, and requiring them would reject those -// models from facading. When present they still flow through via -// `OpaqueFacadeStatics`; when absent the facade simply doesn't offer them. -type OpaqueFacadeInput = S.Top & { - readonly DecodingServices: DecodingServices - readonly EncodingServices: EncodingServices -} - -type OpaqueClassFacadeInput = - & OpaqueFacadeInput< - DecodingServices, - EncodingServices - > - & (abstract new(...args: Array) => unknown) - -type PrototypeFunction = Function & { prototype: object } - -// Class schemas expose a constructor descriptor under this key (was previously -// identified via a `~effect/Schema/Class` annotation id). -const ConstructorAnnotationKey = "~constructor" - -const getOwnOrInheritedPropertyDescriptor = (value: object, property: PropertyKey): PropertyDescriptor | undefined => { - let current: object | null = value - while (current !== null) { - const descriptor = Object.getOwnPropertyDescriptor(current, property) - if (descriptor !== undefined) return descriptor - current = Object.getPrototypeOf(current) - } -} - -const isClassSchemaConstructor = (value: unknown): value is PrototypeFunction => { - if (typeof value !== "function") return false - // Transformed schemas (e.g. `Class.pipe(S.encodeKeys(...))`) are also functions with a - // Declaration AST that still carries `~constructor`, but they are not constructable classes. - // Real Schema.Class constructors expose a static `identifier`. - if (typeof (value as { readonly identifier?: unknown }).identifier !== "string") return false - const ast = (value as { readonly ast?: unknown }).ast - return SchemaAST.isAST(ast) - && SchemaAST.isDeclaration(ast) - && ast.annotations?.[ConstructorAnnotationKey] !== undefined -} - -const getFacadeClassSchema = (schema: OpaqueFacadeInput): PrototypeFunction | undefined => { - if (isClassSchemaConstructor(schema)) return schema - const target = "to" in schema ? (schema as { readonly to?: unknown }).to : undefined - return isClassSchemaConstructor(target) ? target : undefined -} - -const makePublicClassTransformation = (Public: PrototypeFunction) => - new SchemaTransformation.Transformation( - SchemaGetter.transform((input) => Reflect.construct(Public, [input], Public)), - SchemaGetter.passthrough() - ) - -const makeFacadeClassAst = ( - schema: OpaqueFacadeInput, - originalAst: SchemaAST.AST, - Public: PrototypeFunction -): SchemaAST.AST => { - if (SchemaAST.isDeclaration(schema.ast)) { - const schemaEncoding = schema.ast.encoding ?? [] - if (schemaEncoding.length > 0) { - return new SchemaAST.Declaration( - schema.ast.typeParameters, - schema.ast.run, - schema.ast.annotations, - schema.ast.checks, - [new SchemaAST.Link(schemaEncoding[0]!.to, makePublicClassTransformation(Public)), ...schemaEncoding.slice(1)], - schema.ast.context, - schema.ast.encodingChecks - ) - } - } - - return schema.rebuild(originalAst).ast -} - -// Carry the schema's own statics, dropping the generic `S.Top` machinery and -// `prototype`. EXCEPT `to`: models compose each other via `X.to.fields` / -// `X.to.copy` at definition time, so it must survive on the facade. -type OpaqueFacadeStatics = Omit | "prototype"> - -/** - * Like {@link OpaqueType}, but ALSO supplies a static **make-input** shape, so - * `make`/`copy` resolve a named `MakeIn` interface instead of re-deriving the struct's - * mapped `~type.make.in`. Use with codegen-supplied `X.Type` / `X.Encoded` / `X.Make`: - * - * ```ts - * class X extends OpaqueShape()(struct) {} - * ``` - * - * `decode`/`encode` already use the supplied `Type`/`Encoded` (they read the schema's - * `Type`/`Encoded` members, which are `Self`/`Encoded` here) — no separate override needed. - * - * NOTE — measured gain is modest (~5%): the struct passed to the wrapper is still - * constructed in full (definition cost dominates); the static shapes only cheapen - * consumer-side reads of `Type`/`Encoded`/`MakeIn`. Same `no-branding` gap as - * {@link OpaqueType}. - */ -export interface OpaqueShape - extends S.Opaque -{ - readonly "Encoded": Encoded - readonly "~type.make.in": MakeIn - new(_: never): Self -} - -export const OpaqueShape: () => ( - schema: S -) => OpaqueShape & Omit = S.Opaque as any - -/** - * Shallow public view for generated model facades. - * - * The runtime value can still be the full private schema class, but emitted - * declarations expose only named `Type` / `Encoded` / `Make` interfaces and a - * small static surface. This keeps downstream project references from pulling - * the private struct field map back through `typeof Model`. - */ -export interface OpaqueFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - new(_: never): Brand - readonly copy: ReturnType Self>> - // NOTE: no `identifier` here — `S.Opaque` (and requests) build on `S.Bottom`, not `S.Class`, - // so they have no `identifier`. It lives only on the class facades below. - // NOTE: `fields` / `mapFields` are intentionally NOT redeclared here. They are - // carried (precise) from the underlying schema via `OpaqueFacadeStatics`. A wide - // `mapFields(f: (fields: S.Struct.Fields) => To)` override would win overload - // resolution and erase field precision in `Q.project(X.mapFields(...))`. -} - -export interface OpaqueClassFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - new(...args: OpaqueFacadeConstructorArgs): Brand - readonly copy: ReturnType Self>> - readonly identifier: string -} - -export function OpaqueFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} ->() { - function facade>( - schema: SchemaS - ): - & OpaqueClassFacade - & OpaqueFacadeStatics - function facade>( - schema: SchemaS - ): - & OpaqueFacade - & OpaqueFacadeStatics - function facade(schema: OpaqueFacadeInput) { - const Base = getFacadeClassSchema(schema) - if (Base !== undefined) { - const astCache = new WeakMap() - const originalAstDescriptor = getOwnOrInheritedPropertyDescriptor(Base, "ast") - - return class extends (Base as any) { - static get ast(): SchemaAST.AST { - let cached = astCache.get(this) - if (cached !== undefined) return cached - - const originalAst = originalAstDescriptor?.get?.call(this) as SchemaAST.AST - cached = makeFacadeClassAst(schema, originalAst, this) - astCache.set(this, cached) - return cached - } - } - } - - class Facade {} - return Object.setPrototypeOf(Facade, schema) - } - - return facade -} - -/** - * Like class-schema {@link OpaqueFacade}, but for error models (`TaggedErrorClass` / - * `ErrorClass`). The decoded instance type carries `Cause.YieldableError`, so - * `yield* new MyError(...)`, `Effect.fail(myError)`, and `instanceof` all keep - * working through the facade — the runtime `_X` is the real error class (the - * facade `X extends ...(_X)` inherits its prototype), and the type reflects it. - * Nothing is lost vs the underlying error class. - */ -export interface OpaqueErrorFacadeClass< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - // YieldableError (not Self) on the constructed instance — like class-schema OpaqueFacade's - // `new(): Brand`, the data type comes from the declaration-merged `interface X`, - // so `Self` must NOT appear here (would recurse). Merging `X & YieldableError` - // makes `yield* new X()` / `Effect.fail` / `instanceof` work without losing data. - new(...args: OpaqueFacadeConstructorArgs): Cause.YieldableError & Brand - readonly copy: ReturnType Self>> - readonly identifier: string -} - -export function OpaqueErrorFacadeClass< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} ->() { - return >( - schema: SchemaS - ): - & OpaqueErrorFacadeClass - & OpaqueFacadeStatics => - { - type FacadeSchema = - & OpaqueErrorFacadeClass - & OpaqueFacadeStatics - - if (isClassSchemaConstructor(schema)) { - return schema as SchemaS & FacadeSchema - } - - throw new TypeError("OpaqueErrorFacadeClass requires a class schema") - } -} - -/** - * Workflow-compatible struct facade. - * - * A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` schema model whose `.d.ts` - * is produced by the schema-aware declaration emitter is retyped from the giant inline - * `S.Struct<{ ...fields... }>` to this facade. - * - * Unlike {@link OpaqueFacade}, `StructFacade` **extends effect-app's `Struct`**, so the - * value is still a real (effect-app) struct schema: it satisfies `Workflow.AnyStructSchema` and - * the `Struct` reconstruction the workflow machinery performs, and `Union` / - * `.fields.x` keep working. It MUST be effect-app's `Struct` (not effect core's) — scanner - * models are effect-app `S.Struct`, so a facade built on `effect/Schema`'s `Struct` is not - * assignable where an effect-app struct is expected. At the same time the type-level members - * (`Type` / `Encoded` / make-input / services) are pinned to the named interfaces the emitter - * generates in `namespace X`, so consumers reference those by name instead of re-deriving them - * from `Fields` on every use. `Fields` is the model's own generated `X.Fields` interface — a - * single materialization of the (expensive) field shape that every reference site points at. - */ -export type StructFacade< - Self, - Encoded, - MakeIn, - DecodingServices, - EncodingServices, - Fields extends S.Struct.Fields -> = - & Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> - & { - readonly "Type": Self - readonly "Encoded": Encoded - readonly "~type.make.in": MakeIn - readonly "DecodingServices": DecodingServices - readonly "EncodingServices": EncodingServices - } diff --git a/packages/effect-app/src/Schema/FastCheck.ts b/packages/effect-app/src/Schema/FastCheck.ts deleted file mode 100644 index 0c01c89aa..000000000 --- a/packages/effect-app/src/Schema/FastCheck.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type * as fc from "fast-check" - -export type { Arbitrary } from "fast-check" -type FC = typeof fc - -export type { FC } - -export const ALPHABET = "abcdefghijklmnopqrstuvwxyz" -export const ALPHABET_UC = ALPHABET.toUpperCase() - -// uppercase A-Z -const UC_char = (fc: Pick) => fc.integer({ min: 0x41, max: 0x5a }).map(String.fromCharCode) -export const UC_alphabet = (min: number, max: number) => (fc: FC) => - fc.array(UC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) -// lowercase A-Z -const LC_char = (fc: Pick) => fc.integer({ min: 0x61, max: 0x7a }).map(String.fromCharCode) -export const LC_alphabet = (min: number, max: number) => (fc: Pick) => - fc.array(LC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) - -// numbers 0-9 -const N_char = (fc: Pick) => fc.integer({ min: 0x30, max: 0x39 }).map(String.fromCharCode) -export const Numbers = (min: number, max: number) => (fc: Pick) => - fc.array(N_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) diff --git a/packages/effect-app/src/Schema/Methods.ts b/packages/effect-app/src/Schema/Methods.ts deleted file mode 100644 index 2da6a7a93..000000000 --- a/packages/effect-app/src/Schema/Methods.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type ReadMethods = "GET" -export type WriteMethods = "POST" | "PUT" | "PATCH" | "DELETE" - -export type Rest = ReadMethods | WriteMethods - -export type System = "OPTIONS" | "HEAD" | "TRACE" - -export type All = Rest | System diff --git a/packages/effect-app/src/Schema/SchemaParser.ts b/packages/effect-app/src/Schema/SchemaParser.ts deleted file mode 100644 index 5b0fe3265..000000000 --- a/packages/effect-app/src/Schema/SchemaParser.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as SchemaParser from "effect/SchemaParser" -import { withDefaultParseOptions } from "./ext.ts" - -export * from "effect/SchemaParser" - -export const decodeEffectConcurrently: typeof SchemaParser.decodeEffect = withDefaultParseOptions( - SchemaParser.decodeEffect -) - -export const decodeUnknownEffectConcurrently: typeof SchemaParser.decodeUnknownEffect = withDefaultParseOptions( - SchemaParser.decodeUnknownEffect -) diff --git a/packages/effect-app/src/Schema/SpecialJsonSchema.ts b/packages/effect-app/src/Schema/SpecialJsonSchema.ts deleted file mode 100644 index e99d1cd88..000000000 --- a/packages/effect-app/src/Schema/SpecialJsonSchema.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SpecialJsonSchema — A variant of Schema.toJsonSchemaDocument that - * post-processes the output (e.g. flattens simple allOf). - */ -import type * as JsonSchema from "effect/JsonSchema" -import * as Schema from "effect/Schema" - -/** - * Converts a schema to a JSON Schema Document (draft-2020-12), with - * post-processing that flattens simple allOf entries. - */ -export function specialJsonSchemaDocument( - schema: Schema.Top, - options?: Schema.ToJsonSchemaOptions -): JsonSchema.Document<"draft-2020-12"> { - const jd = Schema.toJsonSchemaDocument(schema, options) - const processedDefs: JsonSchema.Definitions = {} - for (const [key, def] of Object.entries(jd.definitions)) { - processedDefs[key] = postProcessJsonSchema(def) - } - return { - dialect: "draft-2020-12", - schema: postProcessJsonSchema(jd.schema), - definitions: processedDefs - } -} - -/** - * Flattens `allOf` entries into the parent when the parent already has a - * `type` and every `allOf` entry is a plain constraint object (no `$ref`, - * no `type`). Merged properties from `allOf` entries win on conflict. - */ -export function flattenSimpleAllOf(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - - if (globalThis.Array.isArray(obj)) { - return obj.map(flattenSimpleAllOf) - } - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - result[key] = flattenSimpleAllOf(value) - } - - if (result["type"] && globalThis.Array.isArray(result["allOf"])) { - const allOf = result["allOf"] as Array> - const canFlatten = allOf.every((entry) => - typeof entry === "object" && entry !== null && !("$ref" in entry) && !("type" in entry) - ) - if (canFlatten) { - const { allOf: _, ...rest } = result - const merged: Record = { ...rest } - for (const entry of allOf) { - Object.assign(merged, entry) - } - return merged - } - } - - return result -} - -/** - * Recursively removes `additionalProperties: false` from JSON Schema objects. - * Only removes when the value is exactly `false` -- other values are left intact. - */ -export function removeAdditionalPropertiesFalse(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - - if (globalThis.Array.isArray(obj)) { - return obj.map(removeAdditionalPropertiesFalse) - } - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - if (key === "additionalProperties" && value === false) continue - result[key] = removeAdditionalPropertiesFalse(value) - } - - return result -} - -/** - * Flattens nested `anyOf` entries: if an anyOf entry is itself just `{ anyOf: [...] }` - * with no other keys, its children are inlined. If only one item remains, the anyOf - * wrapper is removed entirely. - */ -export function flattenNestedAnyOf(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - if (globalThis.Array.isArray(obj)) return obj.map(flattenNestedAnyOf) - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - result[key] = flattenNestedAnyOf(value) - } - - if (globalThis.Array.isArray(result["anyOf"])) { - const anyOf = result["anyOf"] as Array - const flattened: Array = [] - for (const entry of anyOf) { - if ( - typeof entry === "object" - && entry !== null - && !globalThis.Array.isArray(entry) - && "anyOf" in entry - && Object.keys(entry).length === 1 - && globalThis.Array.isArray((entry as Record)["anyOf"]) - ) { - flattened.push(...(entry as Record)["anyOf"] as Array) - } else { - flattened.push(entry) - } - } - if (flattened.length === 1) { - const { anyOf: _, ...rest } = result - const single = flattened[0] - if (typeof single === "object" && single !== null && !globalThis.Array.isArray(single)) { - return { ...rest, ...single } - } - return single - } - result["anyOf"] = flattened - } - - return result -} - -/** - * Applies JSON Schema post-processing: flattens simple allOf, - * flattens nested anyOf, then strips additionalProperties: false. - */ -export function postProcessJsonSchema(obj: JsonSchema.JsonSchema): JsonSchema.JsonSchema { - return removeAdditionalPropertiesFalse(flattenNestedAnyOf(flattenSimpleAllOf(obj))) as JsonSchema.JsonSchema -} diff --git a/packages/effect-app/src/Schema/SpecialOpenApi.ts b/packages/effect-app/src/Schema/SpecialOpenApi.ts deleted file mode 100644 index 44c88e372..000000000 --- a/packages/effect-app/src/Schema/SpecialOpenApi.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec - * and applies JSON Schema post-processing (null removal, allOf flattening). - * - * When `OpenApi.fromApi` generates the spec, different AST nodes sharing the - * same identifier can produce duplicate entries (e.g. "X" and "X1") in - * `components.schemas`. This module provides a transform function that - * collapses those duplicates, rewrites all `$ref` pointers accordingly, - * and post-processes schemas for better codegen compatibility. - * - * Usage with the OpenApi `Transform` annotation: - * - * ```ts - * import { OpenApi } from "effect/http-api" - * import { deduplicateOpenApiSchemas } from "./SpecialOpenApi.ts" - * - * const api = HttpApi.make("myApi") - * .pipe(HttpApi.annotateContext(OpenApi.annotations({ transform: deduplicateOpenApiSchemas }))) - * ``` - */ - -import { postProcessJsonSchema } from "./SpecialJsonSchema.ts" - -/** - * Deduplicates `components.schemas` entries in an OpenAPI spec. - * - * Entries sharing the same base identifier (e.g. "X" and "X1") whose JSON - * representations are identical are collapsed into a single canonical entry, - * and all `$ref` pointers throughout the spec are rewritten to point to - * the canonical key. - * - * Designed to be used as the `transform` option in `OpenApi.annotations`. - */ -export function deduplicateOpenApiSchemas( - spec: Record -): Record { - const components = spec["components"] as Record | undefined - if (!components) return spec - const schemas = components["schemas"] as Record | undefined - if (!schemas) return spec - - const keys = Object.keys(schemas) - if (keys.length === 0) return spec - - // Group keys by base identifier (strip trailing digits) - const groups = new Map>() - for (const key of keys) { - const base = getBaseIdentifier(key) - const fingerprint = JSON.stringify(schemas[key]) - const group = groups.get(base) - if (group === undefined) { - groups.set(base, [{ key, fingerprint }]) - } else { - group.push({ key, fingerprint }) - } - } - - // Build remapping from duplicate keys to canonical keys - const remapping = new Map() - for (const [, group] of groups) { - if (group.length <= 1) continue - const seen = new Map() // fingerprint -> canonical key - for (const entry of group) { - const canonical = seen.get(entry.fingerprint) - if (canonical !== undefined) { - remapping.set(entry.key, canonical) - } else { - seen.set(entry.fingerprint, entry.key) - } - } - } - - if (remapping.size === 0) return postProcessJsonSchema(spec) - - // Build new schemas object without duplicates - const newSchemas: Record = {} - for (const key of keys) { - if (!remapping.has(key)) { - newSchemas[key] = schemas[key] - } - } - - // Deep clone the spec, replace schemas, and rewrite all $ref pointers - const newSpec = structuredClone(spec) - newSpec["components"]["schemas"] = newSchemas - rewriteRefs(newSpec, remapping) - - return postProcessJsonSchema(newSpec) -} - -/** - * Extracts the base identifier from a schema key by stripping trailing - * digits appended by the gen() function. - * E.g. "X1" -> "X", "X" -> "X", "MyType2" -> "MyType" - */ -function getBaseIdentifier(key: string): string { - const match = key.match(/^(.+?)(\d+)$/) - return match ? match[1]! : key -} - -/** - * Recursively rewrites `$ref` values in a JSON object tree. - * Mutates the object in-place (caller should pass a deep clone). - */ -function rewriteRefs(obj: any, remapping: Map): void { - if (obj === null || typeof obj !== "object") return - - if (Array.isArray(obj)) { - for (const item of obj) { - rewriteRefs(item, remapping) - } - return - } - - if (typeof obj.$ref === "string") { - // OpenAPI refs look like "#/components/schemas/X1" - const prefix = "#/components/schemas/" - if (obj.$ref.startsWith(prefix)) { - const refKey = obj.$ref.slice(prefix.length) - const canonical = remapping.get(refKey) - if (canonical !== undefined) { - obj.$ref = prefix + canonical - } - } - } - - for (const value of Object.values(obj)) { - rewriteRefs(value, remapping) - } -} diff --git a/packages/effect-app/src/Schema/brand.ts b/packages/effect-app/src/Schema/brand.ts deleted file mode 100644 index bd4275112..000000000 --- a/packages/effect-app/src/Schema/brand.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -import * as B from "effect/Brand" -import type * as Option from "effect/Option" -import type * as Result from "effect/Result" -import * as S from "effect/Schema" - -export interface Constructor> { - /** - * Constructs a branded type from a value of type `A`, throwing an error if - * the provided `A` is not valid. - */ - (args: Unbranded): A - /** - * Constructs a branded type from a value of type `A`, returning `Some` - * if the provided `A` is valid, `None` otherwise. - */ - option(args: Unbranded): Option.Option - /** - * Constructs a branded type from a value of type `A`, returning `Result.succeed` - * if the provided `A` is valid, `Result.fail` otherwise. - */ - result(args: Unbranded): Result.Result - /** - * Attempts to refine the provided value of type `A`, returning `true` if - * the provided `A` is valid, `false` otherwise. - */ - is(a: Unbranded): a is Unbranded & A -} - -type BrandAnnotations = S.Annotations.Filter - -export interface BrandedSchema> extends - S.Bottom< - C, - S["Encoded"], - S["DecodingServices"], - S["EncodingServices"], - S["ast"], - BrandedSchema, - S["~type.make.in"], - S["Iso"], - S["~type.parameters"], - C, - S["~type.mutability"], - S["~type.optionality"], - S["~type.constructor.default"], - S["~encoded.mutability"], - S["~encoded.optionality"] - > -{} - -export const fromBrand = >( - _constructor: Constructor, - options?: BrandAnnotations -) => -(self: Self): BrandedSchema => { - const branded = self.pipe(S.brand("Brand")) - return (options ? branded.pipe(S.annotate(options)) : branded) as BrandedSchema -} - -export type Brands

= P extends B.Brand ? B.Brand.Brands

- : never - -export type Unbranded

= P extends B.Brand ? B.Brand.Unbranded

> = { -// [K in keyof P[B.BrandTypeId]]: K extends string | symbol ? { -// readonly [k in K]: k -// } -// : never -// }[keyof P[B.BrandTypeId]] -// export type UnionToIntersection3 = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I -// : never - -/** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */ -export const inputDate = extendM( - S.Union([S.Date, DateFromString]), - (s) => ({ - /** - * Construction-only default `new Date()`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — cannot be - * used to JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))), - /** - * Decode-time default `new Date()`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; - * silently substituting `new Date()` hides the problem. Prefer an - * explicit, preferably versioned migration over a decode-time fallback. - * See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date()))) - }) -) - -export interface UnionBrand {} - -// TODO: v4 migration — makeOpt used internal PropertySignature types that are removed in v4 -// Simplified to use v4's S.optional / S.optionalKey directly -export function makeOptional( - t: NER -): { - [K in keyof NER]: NER[K] extends S.Top ? ReturnType> : any -} { - return typedKeysOf(t).reduce((prev, cur) => { - prev[cur] = S.optional(t[cur] as any) - return prev - }, {} as any) -} - -// `Omit` erases the polymorphic `this` of "this-returning" methods (`annotate`, `annotateKey`, -// `check`, `rebuild`): extracting them through a mapped type binds their `this["Rebuild"]` back to -// the original `T`, not to this intersection. So overriding just `Rebuild` above is not enough, those -// four methods have to be redeclared here with a concrete return type instead of `this["Rebuild"]`. -export type DropConstructorDefault = - & Omit< - T, - "~type.constructor.default" | "Rebuild" | "annotate" | "annotateKey" | "check" | "rebuild" - > - & { - readonly "~type.constructor.default": "no-default" - readonly "Rebuild": DropConstructorDefault - annotate(annotations: S.Annotations.Bottom): DropConstructorDefault - annotateKey(annotations: S.Annotations.Key): DropConstructorDefault - check( - ...checks: readonly [SchemaAST.Check, ...Array>] - ): DropConstructorDefault - rebuild(ast: T["ast"]): DropConstructorDefault - } - -// `replaceContext` is `@internal` in `effect/SchemaAST` (stripped from its public types, though the -// export itself still ships in the compiled JS). Effect's own `withConstructorDefault` goes through the -// same function internally, so this mirrors an existing code path rather than reaching for something -// novel, but it's still an internal API that could move or disappear without a signal. -const SchemaASTInternal = SchemaAST as unknown as { - readonly replaceContext: (ast: A, context: SchemaAST.Context | undefined) => A -} - -export interface DropConstructorDefaultLambda extends Struct.Lambda { - (self: T): DropConstructorDefault - readonly "~lambda.out": DropConstructorDefault -} - -/** - * A `Struct.Lambda` that removes a field's `withConstructorDefault`, if any. - * Compose with `Struct.pick`/`Struct.omit` via `Struct.map` to derive a - * partial-update schema from a "create" schema without inheriting defaults - * that would resurrect omitted fields in `.make(...)` output: - * - * ```ts - * import { flow } from "effect/Function" - * import * as Struct from "effect/Struct" - * import * as S from "effect-app/Schema" - * - * const Person = S.Struct({ - * id: S.String, - * name: S.String.pipe(S.optionalKey, S.withConstructorDefault(() => "")) - * }) - * - * const UpdatePerson = Person.mapFields( - * flow(Struct.omit(["id"]), Struct.map(S.dropConstructorDefault)) - * ) - * ``` - */ -export const dropConstructorDefault: DropConstructorDefaultLambda = Struct.lambda( - (schema) => { - const context = schema.ast.context - if (!context?.constructorDefault) return schema - return schema.rebuild( - SchemaASTInternal.replaceContext( - schema.ast, - new SchemaAST.Context(context.isOptional, context.isMutable, undefined, context.annotations) - ) - ) - } -) - -export function makeExactOptional( - t: NER -): { - [K in keyof NER]: NER[K] extends S.Top ? DropConstructorDefault>> - : any -} { - return typedKeysOf(t).reduce((prev, cur) => { - prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any)) - return prev - }, {} as any) -} - -/** A version of transform which is only a one way mapping of From->To */ -export const transformTo = ( - from: From, - to: To, - decode: ( - fromA: From["Type"], - options: SchemaAST.ParseOptions - ) => To["Encoded"] -) => - from.pipe( - S.decodeTo( - to, - SchemaTransformation.transformEffect({ - decode: (input: any, options: any) => Effect.sync(() => decode(input, options)), - encode: (i: any) => - Effect.fail( - new SchemaIssue.Forbidden( - { message: "One way schema transformation, encoding is not allowed" }, - i - ) - ) - }) - ) - ) - -/** A version of transformEffect which is only a one way mapping of From->To */ -export const transformToOrFail = ( - from: From, - to: To, - decode: ( - fromA: From["Type"], - options: SchemaAST.ParseOptions - ) => Effect.Effect -) => - from.pipe( - S.decodeTo( - to, - SchemaTransformation.transformEffect({ - decode, - encode: (i: any) => - Effect.fail( - new SchemaIssue.Forbidden( - { message: "One way schema transformation, encoding is not allowed" }, - i - ) - ) - }) - ) - ) - -export const provide: { - (context: Context.Context): (self: Self) => ProvidedCodec - (self: Self, context: Context.Context): ProvidedCodec -} = Function.dual(2, (self: Self, context: Context.Context): ProvidedCodec => { - const prov = Effect.provide(context) - return self.pipe( - S.middlewareDecoding((effect) => prov(effect)), - S.middlewareEncoding((effect) => prov(effect)) - ) -}) -export const contextFromServices = Effect.fnUntraced(function*< - Self extends S.Top, - Tags extends ReadonlyArray> ->(self: Self, ...services: Tags) { - const context: Context.Context> = Context.pick(...services)( - yield* Effect.context>() - ) - return provide(self, context) -}) as < - Self extends S.Top, - Tags extends ReadonlyArray> ->( - self: Self, - ...services: Tags -) => Effect.Effect< - ProvidedCodec>, - never, - Context.Service.Identifier -> diff --git a/packages/effect-app/src/Schema/moreStrings.ts b/packages/effect-app/src/Schema/moreStrings.ts deleted file mode 100644 index ece539d1e..000000000 --- a/packages/effect-app/src/Schema/moreStrings.ts +++ /dev/null @@ -1,390 +0,0 @@ -/** - * Branded string ID schemas with `.withConstructorDefault` extensions. - * - * Each `.withConstructorDefault` here is **only** applied when the field is - * omitted during construction (`.make(...)`). It is **not** applied during - * decode and therefore cannot be used to JIT-migrate database fields. - * - * For persisted data, prefer an explicit, preferably versioned migration - * over decode-time fallbacks. See `./ext.ts` for the full policy note. - */ -import type { Refinement } from "effect-app/Function" -import { extendM } from "effect-app/utils" -import type * as B from "effect/Brand" -import * as Effect from "effect/Effect" -import { pipe } from "effect/Function" -import * as S from "effect/Schema" -import * as Getter from "effect/SchemaGetter" -import * as SchemaIssue from "effect/SchemaIssue" -import type { Simplify } from "effect/Types" -import { customRandom, nanoid, urlAlphabet } from "nanoid" -import validator from "validator" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" -import { type B as SchemaB } from "./schema.ts" -import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.ts" - -const nonEmptyString = S.NonEmptyString - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export interface NonEmptyString50Brand extends Simplify & NonEmptyString64Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export type NonEmptyString50 = string & NonEmptyString50Brand - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export interface NonEmptyString50Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString50 -} -export const NonEmptyString50: NonEmptyString50Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(50)), - fromBrand(nominal(), { - identifier: "NonEmptyString50", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export interface NonEmptyString64Brand extends Simplify & NonEmptyString80Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export type NonEmptyString64 = string & NonEmptyString64Brand - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export interface NonEmptyString64Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString64 -} -export const NonEmptyString64: NonEmptyString64Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(64)), - fromBrand(nominal(), { - identifier: "NonEmptyString64", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 80. - */ -export interface NonEmptyString80Brand extends Simplify & NonEmptyString100Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 80. - */ -export type NonEmptyString80 = string & NonEmptyString80Brand - -/** - * A string that is at least 1 character long and a maximum of 80. - */ - -export interface NonEmptyString80Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString80 -} -export const NonEmptyString80: NonEmptyString80Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(80)), - fromBrand(nominal(), { - identifier: "NonEmptyString80", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export interface NonEmptyString100Brand extends Simplify & NonEmptyString255Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export type NonEmptyString100 = string & NonEmptyString100Brand - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export interface NonEmptyString100Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString100 -} -export const NonEmptyString100: NonEmptyString100Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(100)), - fromBrand(nominal(), { - identifier: "NonEmptyString100", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export interface Min3String255Brand extends Simplify & NonEmptyString255Brand> {} - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export type Min3String255 = string & Min3String255Brand - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export interface Min3String255Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): Min3String255 -} -export const Min3String255: Min3String255Schema = pipe( - S.String, - S.check(S.isMinLength(3), S.isMaxLength(255)), - fromBrand(nominal(), { - identifier: "Min3String255", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 6 characters long and a maximum of 50. - */ -export interface StringIdBrand extends Simplify & NonEmptyString50Brand> {} - -/** - * A string that is at least 6 characters long and a maximum of 50. - */ -export type StringId = string & StringIdBrand - -const minLength = 6 -const maxLength = 50 -const nanoidSize = 21 -const nanoidByteLength = 10 * nanoidSize - -const StringIdString = pipe( - S.String, - S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)), - fromBrand(nominal(), { - identifier: "StringId", - jsonSchema: {} - }) -) - -const nanoidFromBytes = (bytes: Uint8Array) => - customRandom(urlAlphabet, nanoidSize, (size) => bytes.subarray(0, size))() as StringId - -// JSON stays a branded string (toCodec). Generation uses toCodecArbitrary — the native -// replacement for fast-check `toArbitrary` — same as the old StringIdArb: -// uint8Array(210).map(bytes => customRandom(urlAlphabet, 21, …)). -const StringIdSchemaBase = S.declareConstructor()( - [], - () => (input, ast, options) => - S.is(StringIdString)(input) - ? Effect.succeed(input) - : Effect.fail(new SchemaIssue.InvalidType(ast, input, options)), - { - identifier: "StringId", - expected: "StringId", - toCodec: () => - S.link()(StringIdString, { - decode: Getter.passthrough(), - encode: Getter.passthrough() - }), - toCodecArbitrary: () => - S.link()( - S.Uint8Array.check(S.isMinLength(nanoidByteLength), S.isMaxLength(nanoidByteLength)), - { - decode: Getter.transform(nanoidFromBytes), - encode: Getter.forbiddenEncoding - } - ) - } -) -const makeStringId = (s?: string): StringId => - s !== undefined ? S.decodeSync(StringIdString)(s) : nanoid() as unknown as StringId -/** - * A string that is at least 6 characters long and a maximum of 50. - * - * `.withConstructorDefault` => fresh `nanoid()` (construction-only; not - * applied during decode — see file-level note). - */ -export interface StringIdSchema extends S.declareConstructor { - (i: string, options?: SchemaAST.ParseOptions): StringId - /** Generate fresh `nanoid()`-shaped `StringId`. */ - make(): StringId - /** Construct a `StringId` from a known string (validated via decodeSync). */ - make(input: string, options?: S.MakeOptions): StringId - readonly withConstructorDefault: S.withConstructorDefault< - S.declareConstructor - > -} -export const StringId: StringIdSchema = extendM( - StringIdSchemaBase, - (s) => ({ - make: makeStringId, - /** - * Construction-only default: fresh `nanoid()`-shaped `StringId`. Applied - * only when the field is omitted from `.make(...)` input. NOT applied - * during decode — cannot be used to JIT-migrate database fields. See - * file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId))) - }) -) - .pipe(withDefaultMake) - -/** - * Build a `StringId` schema whose values are required to start with a fixed - * `prefix` (joined with `separator`, default `-`). - * - * The returned schema exposes `.withConstructorDefault` that mints a fresh - * prefixed id. Construction-only — not applied during decode; see file-level - * note. - */ -export function prefixedStringId() { - return ( - prefix: Prefix, - name: string, - separator?: Separator - ) => { - type FullPrefix = `${Prefix}${Separator}` - const pref = `${prefix}${separator ?? "-"}` as FullPrefix - const PrefixedString = StringIdString.pipe( - S.refine((x): x is Type => x.startsWith(pref), { identifier: name }) - ) - // Pre-rc.114: StringIdArb().map(x => pref + x.substring(0, 50 - pref.length)) - const s = S.declareConstructor()( - [], - () => (input, ast, options) => - S.is(PrefixedString)(input) - ? Effect.succeed(input) - : Effect.fail(new SchemaIssue.InvalidType(ast, input, options)), - { - identifier: name, - expected: name, - toCodec: () => - S.link()(PrefixedString, { - decode: Getter.passthrough(), - encode: Getter.passthrough() - }), - toCodecArbitrary: () => - S.link()( - S.Uint8Array.check(S.isMinLength(nanoidByteLength), S.isMaxLength(nanoidByteLength)), - { - decode: Getter.transform((bytes) => - (pref + nanoidFromBytes(bytes).substring(0, maxLength - pref.length)) as Type - ), - encode: Getter.forbiddenEncoding - } - ) - } - ) - const schema = s.pipe(withDefaultMake) - const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Type - - return extendM( - schema, - (ex): PrefixedStringUtils => ({ - make, - /** - * Automatically adds the prefix. - */ - unsafeFrom: (str: string) => ex(pref + str), - /** - * Must provide a literal string starting with prefix. - */ - prefixSafe: (str: `${Prefix}${Separator}${REST}`) => ex(str), - prefix, - /** - * Construction-only default: fresh prefixed id. Applied only when - * the field is omitted from `.make(...)` input. NOT applied during - * decode — cannot be used to JIT-migrate database fields. See - * file-level note. - */ - withConstructorDefault: schema.pipe( - S.withConstructorDefault & S.WithoutConstructorDefault>( - Effect.sync(make) - ) - ) - }) - ) - } -} - -/** - * Build a branded `StringId` schema for the given branded `Id` type. - * - * Exposes `.withConstructorDefault` that mints a fresh `nanoid()`-shaped id. - * Construction-only — not applied during decode; see file-level note. - */ -export const brandedStringId = >(): BrandedStringIdSchema => - withDefaultMake( - Object.assign(Object.create(StringId), StringId) as BrandedStringIdSchema - ) - -export interface PrefixedStringUtils< - Type extends StringId, - Prefix extends string, - Separator extends string -> { - readonly make: () => Type - readonly unsafeFrom: (str: string) => Type - prefixSafe: (str: `${Prefix}${Separator}${REST}`) => Type - readonly prefix: Prefix - /** - * Construction-only default: fresh prefixed id. Applied only when the - * field is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level note. - */ - readonly withConstructorDefault: S.withConstructorDefault & S.WithoutConstructorDefault> -} - -export interface BrandedStringIdSchema> extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): Id - /** Generate fresh `nanoid()`-shaped id (inherited from `StringId`). */ - make(): Id - /** Construct an `Id` from a known string (validated via decodeSync). */ - make(input: string, options?: S.MakeOptions): Id - readonly withConstructorDefault: S.withConstructorDefault> -} - -export interface UrlBrand extends Simplify & NonEmptyStringBrand> {} - -export type Url = string & UrlBrand - -const isUrl: Refinement = (s: string): s is Url => { - return validator.default.isURL(s, { require_tld: false }) -} - -export interface UrlSchema extends S.refine { - (i: string, options?: SchemaAST.ParseOptions): Url -} -export const Url: UrlSchema = S - .String - .pipe( - S.annotate({ - title: "Url", - format: "uri" - }), - S.refine(isUrl, { - identifier: "Url", - jsonSchema: { format: "uri" }, - // Native Arbitrary otherwise emits strings that `isURL({ require_tld: false })` - // accepts (`"R"`, `"prototype"`). Restore the old fast-check `fc.webUrl()` shape. - arbitraryConstraint: { - patterns: [{ - source: "^https://[a-z]{3,12}\\.(?:com|net|org|de)(?:/[a-z0-9-]{0,16})?$", - flags: "" - }] - } - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/Schema/numbers.ts b/packages/effect-app/src/Schema/numbers.ts deleted file mode 100644 index 020be91dd..000000000 --- a/packages/effect-app/src/Schema/numbers.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Numeric brand schemas with `.withConstructorDefault` extensions. - * - * Each `.withConstructorDefault` here is **only** applied when the field is - * omitted during construction (`.make(...)`). It is **not** applied during - * decode and therefore cannot be used to JIT-migrate database fields. - * - * For persisted data, prefer an explicit, preferably versioned migration - * over decode-time fallbacks. See `./ext.ts` for the full policy note. - */ -import { extendM } from "effect-app/utils" -import * as Effect from "effect/Effect" -import * as Order from "effect/Order" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" -import { type B } from "./schema.ts" - -export interface PositiveIntBrand - extends Simplify & NonNegativeIntBrand & PositiveNumberBrand> -{} -export type PositiveInt = number & PositiveIntBrand -/** Positive integer. `.withConstructorDefault` => `1` (construction-only). */ -export interface PositiveIntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): PositiveInt - readonly withConstructorDefault: S.withConstructorDefault> -} -export const PositiveInt: PositiveIntSchema = extendM( - S.Int.pipe( - S.check(S.isGreaterThan(0)), - fromBrand(nominal(), { identifier: "PositiveInt", jsonSchema: {} }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `1`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) - }) -) - -export interface NonNegativeIntBrand extends Simplify & IntBrand & NonNegativeNumberBrand> {} -export type NonNegativeInt = number & NonNegativeIntBrand -/** Non-negative integer. `.withConstructorDefault` => `0` (construction-only). */ -export interface NonNegativeIntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): NonNegativeInt - readonly withConstructorDefault: S.withConstructorDefault> -} -export const NonNegativeInt: NonNegativeIntSchema = extendM( - S.Int.pipe( - S.check(S.isGreaterThanOrEqualTo(0)), - fromBrand(nominal(), { - identifier: "NonNegativeInt", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -export interface IntBrand extends Simplify> {} -export type Int = number & IntBrand -/** Integer. `.withConstructorDefault` => `0` (construction-only). */ -export interface IntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): Int - readonly withConstructorDefault: S.withConstructorDefault> -} -export const Int: IntSchema = extendM( - S.Int.pipe(fromBrand(nominal(), { identifier: "Int", jsonSchema: {} }), withDefaultMake), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -export interface PositiveNumberBrand extends Simplify & NonNegativeNumberBrand> {} -export type PositiveNumber = number & PositiveNumberBrand -/** Positive finite number. `.withConstructorDefault` => `1` (construction-only). */ -// Generation-only: native Arbitrary otherwise samples up to Number.MAX_VALUE, so derived totals -// (amount x weight, sums) overflow to Infinity. Validation and JSON Schema are unaffected. -const realisticMaximum = 1_000_000 - -export interface PositiveNumberSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): PositiveNumber - readonly withConstructorDefault: S.withConstructorDefault> -} -export const PositiveNumber: PositiveNumberSchema = extendM( - S.Finite.pipe( - S.check(S.isGreaterThan(0, { - // the full constraint: annotations replace the check default, which carries `order` + `minimum` - arbitraryConstraint: { order: Order.Number, minimum: 0, exclusiveMinimum: true, maximum: realisticMaximum } - })), - fromBrand(nominal(), { - identifier: "PositiveNumber", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `1`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) - }) -) - -export interface NonNegativeNumberBrand extends Simplify> {} -export type NonNegativeNumber = number & NonNegativeNumberBrand -/** Non-negative finite number. `.withConstructorDefault` => `0` (construction-only). */ -export interface NonNegativeNumberSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): NonNegativeNumber - readonly withConstructorDefault: S.withConstructorDefault> -} -export const NonNegativeNumber: NonNegativeNumberSchema = extendM( - S - .Finite - .pipe( - S.check(S.isGreaterThanOrEqualTo(0, { - arbitraryConstraint: { order: Order.Number, minimum: 0, maximum: realisticMaximum } - })), - fromBrand(nominal(), { - identifier: "NonNegativeNumber", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -/** @deprecated Not an actual decimal */ -export const NonNegativeDecimal = NonNegativeNumber -/** @deprecated Not an actual decimal */ -export type NonNegativeDecimal = NonNegativeNumber - -/** @deprecated Not an actual decimal */ -export const PositiveDecimal = PositiveNumber -/** @deprecated Not an actual decimal */ -export type PositiveDecimal = PositiveNumber diff --git a/packages/effect-app/src/Schema/phoneNumber.ts b/packages/effect-app/src/Schema/phoneNumber.ts deleted file mode 100644 index 93533734f..000000000 --- a/packages/effect-app/src/Schema/phoneNumber.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Refinement } from "effect-app/Function" -import { isValidPhone } from "effect-app/validation" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import { withDefaultMake } from "./ext.ts" -import type { B } from "./schema.ts" -import type { NonEmptyStringBrand } from "./strings.ts" - -export interface PhoneNumberBrand extends Simplify & NonEmptyStringBrand> {} -export type PhoneNumber = string & PhoneNumberBrand - -export const PhoneNumber = S - .String - .pipe( - S.annotate({ - title: "PhoneNumber", - description: "a phone number with at least 7 digits", - format: "phone" - }), - S.refine(isValidPhone as Refinement, { - identifier: "PhoneNumber", - description: "a phone number with at least 7 digits", - jsonSchema: { format: "phone" }, - // Native Arbitrary cannot invert `isValidPhone`; steer sampling to digit-only numbers (>= 7 digits). - arbitraryConstraint: { - patterns: [{ source: "^\\+?[1-9][0-9]{6,11}$", flags: "" }] - } - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/Schema/schema.ts b/packages/effect-app/src/Schema/schema.ts deleted file mode 100644 index 5b405c8be..000000000 --- a/packages/effect-app/src/Schema/schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as B from "effect/Brand" -import * as P from "effect/SchemaParser" -import * as AST from "../SchemaAST.ts" - -export { AST, B, P } diff --git a/packages/effect-app/src/Schema/strings.ts b/packages/effect-app/src/Schema/strings.ts deleted file mode 100644 index 71d857dd9..000000000 --- a/packages/effect-app/src/Schema/strings.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type * as B from "effect/Brand" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" - -export type NonEmptyStringBrand = B.Brand<"NonEmptyString"> -export type NonEmptyString = string & NonEmptyStringBrand -export interface NonEmptyStringSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString -} -export const NonEmptyString: NonEmptyStringSchema = S - .NonEmptyString - .pipe( - fromBrand(nominal(), { - identifier: "NonEmptyString", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString64kBrand extends Simplify & NonEmptyStringBrand> {} -export type NonEmptyString64k = string & NonEmptyString64kBrand -export interface NonEmptyString64kSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString64k -} -export const NonEmptyString64k: NonEmptyString64kSchema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(64 * 1024)), - fromBrand(nominal(), { - identifier: "NonEmptyString64k", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString2kBrand extends Simplify & NonEmptyString64kBrand> {} -export type NonEmptyString2k = string & NonEmptyString2kBrand -export interface NonEmptyString2kSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString2k -} -export const NonEmptyString2k: NonEmptyString2kSchema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(2 * 1024)), - fromBrand(nominal(), { - identifier: "NonEmptyString2k", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString255Brand extends Simplify & NonEmptyString2kBrand> {} -export type NonEmptyString255 = string & NonEmptyString255Brand -export interface NonEmptyString255Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString255 -} -export const NonEmptyString255: NonEmptyString255Schema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(255)), - fromBrand(nominal(), { - identifier: "NonEmptyString255", - jsonSchema: {} - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/SchemaAST.ts b/packages/effect-app/src/SchemaAST.ts deleted file mode 100644 index 968094487..000000000 --- a/packages/effect-app/src/SchemaAST.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "effect/SchemaAST" diff --git a/packages/effect-app/src/Set.ts b/packages/effect-app/src/Set.ts deleted file mode 100644 index f03b8138d..000000000 --- a/packages/effect-app/src/Set.ts +++ /dev/null @@ -1,665 +0,0 @@ -// ets_tracing: off - -import * as Array from "effect/Array" -import type * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import type * as Order from "effect/Order" -import { not } from "effect/Predicate" -import type * as Result from "effect/Result" -import { identity, pipe, type Predicate, type Refinement, tuple } from "./Function.ts" - -export function find_( - as: ReadonlySet, - refinement: Refinement -): B | undefined -export function find_(set: ReadonlySet, predicate: Predicate): A | undefined -export function find_(set: ReadonlySet, predicate: Predicate) { - return [...set].find(predicate) -} - -export function findFirst_( - set: ReadonlySet, - refinement: Refinement -): Option.Option -export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option -export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option { - return Option.fromNullishOr([...set].find(predicate)) -} - -export function findFirstMap_( - set: ReadonlySet, - f: (a: A) => Option.Option -): Option.Option { - return Array.findFirst([...set], f) -} - -export type MutableSet = globalThis.Set -export type Set = ReadonlySet - -export const empty: Set = new Set() - -/** - * The set of elements which are in both the first and second set - */ -export function intersection_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { - const elemE = elem_(E) - return (x, y) => { - if (x === empty || y === empty) { - return empty - } - const r = new Set() - x.forEach((e) => { - if (elemE(y, e)) { - r.add(e) - } - }) - return r - } -} - -/** - * The set of elements which are in both the first and second set - */ -export function intersection(E: Equivalence.Equivalence): (r: Set) => (l: Set) => Set { - const i = intersection_(E) - return (x) => (y) => i(x, y) -} - -/** - * Convert a mutable set to a readonly one - */ -export function fromMutable(s: MutableSet): Set { - return new Set(s) -} - -/** - * Convert a set to a mutable one - */ -export function toMutable(s: Set): MutableSet { - return new Set(s) -} - -// /** -// * get Show for set given Show for values -// */ -// export function getShow(S: Show): Show> { -// return { -// show: (s) => { -// let elements = "" -// s.forEach((a) => { -// elements += S.show(a) + ", " -// }) -// if (elements !== "") { -// elements = elements.substring(0, elements.length - 2) -// } -// return `new Set([${elements}])` -// } -// } -// } - -/** - * Convert a set to an Array - */ -export function toArray(O: Order.Order): (set: Set) => ReadonlyArray { - return (x) => { - const r: Array = [] - x.forEach((e) => r.push(e)) - return r.sort(O) - } -} - -/** - * Convert a set to an Array - */ -export function toArray_(x: Set, O: Order.Order): ReadonlyArray { - return toArray(O)(x) -} - -// /** -// * Get Equivalence for Setgiven Equivalence for element -// */ -// export function getEquivalence.Equivalence(E: Equivalence.Equivalence): Equivalence.Equivalence> { -// const subsetE = isSubset_(E) -// return makeEquivalence((x, y) => subsetE(x, y) && subsetE(y, x)) -// } - -interface Next { - readonly done?: boolean - readonly value: A -} - -/** - * true if one or more elements match predicate - */ -export function some(predicate: Predicate): (set: Set) => boolean { - return (set) => { - const values = set.values() - let e: Next - let found = false - while (!found && !(e = values.next() as any).done) { - found = predicate(e.value) - } - return found - } -} - -/** - * true if one or more elements match predicate - */ -export function some_(set: Set, predicate: Predicate): boolean { - return some(predicate)(set) -} - -/** - * Projects a Set through a function - */ -export function map(E: Equivalence.Equivalence): (f: (x: A) => B) => (set: Set) => Set { - const m = map_(E) - return (f) => (set) => m(set, f) -} - -/** - * Projects a Set through a function - */ -export function map_(E: Equivalence.Equivalence): (set: Set, f: (x: A) => B) => Set { - const elemE = elem_(E) - return (set, f) => { - const r = new Set() - set.forEach((e) => { - const v = f(e) - if (!elemE(r, v)) { - r.add(v) - } - }) - return r - } -} - -/** - * true if all elements match predicate - */ -export function every(predicate: Predicate): (set: Set) => boolean { - return (set) => every_(set, predicate) -} - -/** - * true if all elements match predicate - */ -export function every_(set: Set, predicate: Predicate): boolean { - return not(some(not(predicate)))(set) -} - -/** - * Map + Flatten - */ -export function chain( - E: Equivalence.Equivalence -): (f: (x: A) => Set) => (set: Set) => Set { - const c = chain_(E) - return (f) => (set) => c(set, f) -} - -/** - * Map + Flatten - */ -export function chain_( - E: Equivalence.Equivalence -): (set: Set, f: (x: A) => Set) => Set { - const elemE = elem_(E) - return (set, f) => { - const r = new Set() - set.forEach((e) => { - f(e).forEach((e) => { - if (!elemE(r, e)) { - r.add(e) - } - }) - }) - return r - } -} - -/** - * `true` if and only if every element in the first set is an element of the second set - */ -export function isSubset(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { - const i = isSubset_(E) - return (x, y) => i(y, x) -} - -/** - * `true` if and only if every element in the first set is an element of the second set - */ -export function isSubset_(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { - const elemE = elem_(E) - return (x, y) => every((a: A) => elemE(y, a))(x) -} - -/** - * Filter set values using predicate - */ -export function filter( - refinement: Refinement -): (set: Set) => Set -export function filter(predicate: Predicate): (set: Set) => Set -export function filter(predicate: Predicate): (set: Set) => Set { - return (set) => filter_(set, predicate) -} - -/** - * Filter set values using predicate - */ -export function filter_( - set: Set, - refinement: Refinement -): Set -export function filter_(set: Set, predicate: Predicate): Set -export function filter_(set: Set, predicate: Predicate): Set { - const values = set.values() - let e: Next - const r = new Set() - while (!(e = values.next() as any).done) { - const value = e.value - if (predicate(value)) { - r.add(value) - } - } - return r -} - -/** - * Partition set values using predicate - */ -export function partition( - refinement: Refinement -): (set: Set) => readonly [Set, Set] -export function partition( - predicate: Predicate -): (set: Set) => readonly [Set, Set] -export function partition( - predicate: Predicate -): (set: Set) => readonly [Set, Set] { - return (set) => partition_(set, predicate) -} - -/** - * Partition set values using predicate - */ -export function partition_( - set: Set, - refinement: Refinement -): readonly [Set, Set] -export function partition_( - set: Set, - predicate: Predicate -): readonly [Set, Set] -export function partition_( - set: Set, - predicate: Predicate -): readonly [Set, Set] { - const values = set.values() - let e: Next - const right = new Set() - const left = new Set() - while (!(e = values.next() as any).done) { - const value = e.value - if (predicate(value)) { - right.add(value) - } else { - left.add(value) - } - } - return tuple(left, right) -} - -/** - * Test if a value is a member of a set - */ -export function elem_(E: Equivalence.Equivalence): (set: Set, a: A) => boolean { - return (set, a) => { - const values = set.values() - let e: Next - let found = false - while (!found && !(e = values.next() as any).done) { - found = E(a, e.value) - } - return found - } -} - -/** - * Test if a value is a member of a set - */ -export function elem(E: Equivalence.Equivalence): (a: A) => (set: Set) => boolean { - const e = elem_(E) - return (a) => (set) => e(set, a) -} - -/** - * Partition elements according to f - */ -export function partitionMap( - EB: Equivalence.Equivalence, - EC: Equivalence.Equivalence -): (f: (a: A) => Result.Result) => (set: Set) => readonly [Set, Set] { - const pm = partitionMap_(EB, EC) - return (f: (a: A) => Result.Result) => (set: Set) => pm(set, f) -} - -/** - * Partition elements according to f - */ -export function partitionMap_( - EB: Equivalence.Equivalence, - EC: Equivalence.Equivalence -): (set: Set, f: (a: A) => Result.Result) => readonly [Set, Set] { - return (set: Set, f: (a: A) => Result.Result) => { - const values = set.values() - let e: Next - const left = new Set() - const right = new Set() - const hasB = elem_(EB) - const hasC = elem_(EC) - while (!(e = values.next() as any).done) { - const v = f(e.value) - switch (v._tag) { - case "Failure": - if (!hasB(left, v.failure)) { - left.add(v.failure) - } - break - case "Success": - if (!hasC(right, v.success)) { - right.add(v.success) - } - break - } - } - return tuple(left, right) - } -} - -/** - * Form the set difference (`x` - `y`) - */ -export function difference_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { - const elemE = elem_(E) - return (x, y) => filter((a: A) => !elemE(y, a))(x) -} - -/** - * Form the set difference (`x` - `y`) - */ -export function difference(E: Equivalence.Equivalence): (x: Set, y: Set) => Set { - const diff = difference_(E) - return (x, y) => diff(x, y) -} - -/** - * Reduce over the set values - */ -export function reduce( - O: Order.Order -): (b: B, f: (b: B, a: A) => B) => (fa: Set) => B { - const red = reduce_(O) - return (b, f) => (fa) => red(fa, b, f) -} - -/** - * Reduce over the set values - */ -export function reduce_( - O: Order.Order -): (fa: Set, b: B, f: (b: B, a: A) => B) => B { - const toArrayO = toArray(O) - return (fa, b, f) => toArrayO(fa).reduce(f, b) -} - -// /** -// * Fold + Map -// */ -// export function foldMap( -// O: Order.Order, -// M: Identity -// ): (f: (a: A) => M) => (fa: Set) => M { -// const fm = foldMap_(O, M) -// return (f) => (fa) => fm(fa, f) -// } - -// /** -// * Fold + Map -// */ -// export function foldMap_( -// O: Order.Order, -// M: Identity -// ): (fa: Set, f: (a: A) => M) => M { -// const toArrayO = toArray(O) -// return (fa, f) => toArrayO(fa).reduce((b, a) => M.combine(b, f(a)), M.identity) -// } - -/** - * Create a set with one element - */ -export function singleton(a: A): Set { - return new Set([a]) -} - -/** - * Insert a value into a set - */ -export function insert(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const i = insert_(E) - return (a) => (set) => i(set, a) -} - -/** - * Insert a value into a set - */ -export function insert_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - const elemE = elem_(E) - return (set, a) => { - if (!elemE(set, a)) { - const r = new Set(set) - r.add(a) - return r - } else { - return set - } - } -} - -/** - * Delete a value from a set - */ -export function remove(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const rem = remove_(E) - return (a) => (set) => rem(set, a) -} - -/** - * Delete a value from a set - */ -export function remove_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - return (set, a) => filter((ax: A) => !E(a, ax))(set) -} - -/** - * If element is present remove it, if not add it - */ -export function toggle(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const t = toggle_(E) - return (a) => (set) => t(set, a) -} - -/** - * If element is present remove it, if not add it - */ -export function toggle_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - const elemE = elem_(E) - const removeE = remove(E) - const insertE = insert(E) - return (set, a) => (elemE(set, a) ? removeE : insertE)(a)(set) -} - -/** - * Create a set from an array - */ -export function fromArray(E: Equivalence.Equivalence): (as: ReadonlyArray) => Set { - return (as) => { - const len = as.length - const r = new Set() - const has = elem_(E) - for (let i = 0; i < len; i++) { - const a = as[i]! - if (!has(r, a)) { - r.add(a) - } - } - return r - } -} - -/** - * Set compaction, remove none - */ -export function compact(E: Equivalence.Equivalence): (fa: Set>) => Set { - return filterMap(E)(identity) -} - -/** - * Separate elements - */ -export function separate( - EE: Equivalence.Equivalence, - EA: Equivalence.Equivalence -): (fa: Set>) => readonly [Set, Set] { - return (fa) => { - const elemEE = elem_(EE) - const elemEA = elem_(EA) - const left: MutableSet = new Set() - const right: MutableSet = new Set() - fa.forEach((e) => { - switch (e._tag) { - case "Failure": - if (!elemEE(left, e.failure)) { - left.add(e.failure) - } - break - case "Success": - if (!elemEA(right, e.success)) { - right.add(e.success) - } - break - } - }) - return tuple(left, right) - } -} - -/** - * Filter + Map - */ -export function filterMap( - E: Equivalence.Equivalence -): (f: (a: A) => Option.Option) => (fa: Set) => Set { - const fm = filterMap_(E) - return (f) => (fa) => fm(fa, f) -} - -/** - * Filter + Map - */ -export function filterMap_( - E: Equivalence.Equivalence -): (fa: Set, f: (a: A) => Option.Option) => Set { - const elemE = elem_(E) - return (fa, f) => { - const r: MutableSet = new Set() - fa.forEach((a) => { - const ob = f(a) - if (ob._tag === "Some" && !elemE(r, ob.value)) { - r.add(ob.value) - } - }) - return r - } -} - -/** - * Form the union of two sets - */ -export function union_(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { - const elemE = elem_(E) - return (x, y) => { - if (x === empty) { - return y - } - if (y === empty) { - return x - } - const r = new Set(x) - y.forEach((e) => { - if (!elemE(r, e)) { - r.add(e) - } - }) - return r - } -} - -/** - * Form the union of two sets - */ -export function union(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { - const u = union_(E) - return (x, y) => u(x, y) -} - -function make_(ord: Order.Order, eq_?: Equivalence.Equivalence) { - const eq = eq_ ?? ((x, y) => ord(x, y) === 0) - - const fromArray_ = fromArray(eq) - const concat_ = (set: Set, it: Iterable) => fromArray_([...set, ...it]) - const insert__ = insert(eq) - - function replace_(set: Set, a: A) { - return pipe(filter_(set, (x) => !eq(x, a)), insert__(a)) - } - - return { - insert: insert__, - insert_: insert_(eq), - remove: remove(eq), - remove_: remove_(eq), - reduce: reduce(ord), - reduce_: reduce_(ord), - replace: (a: A) => (set: Set) => replace_(set, a), - replace_, - toArray: toArray(ord), - fromArray, - from: (it: Iterable) => fromArray_([...it]), - empty: () => new Set(), - concat_, - concat: (it: Iterable) => (set: Set) => concat_(set, it), - - // A and B the same, useful when editing elements. - map: map(eq), - map_: map_(eq), - filterMap: filterMap(eq), - filterMap_: filterMap_(eq) - } - // TODO: extend -} - -class Wrapper { - wrapped(ord: Order.Order, eq: Equivalence.Equivalence) { - return make_(ord, eq) - } -} - -export interface SetSchemaExtensions extends ReturnType["wrapped"]> {} - -export const make: ( - ord: Order.Order, - eq?: Equivalence.Equivalence -) => SetSchemaExtensions = make_ diff --git a/packages/effect-app/src/Store.ts b/packages/effect-app/src/Store.ts deleted file mode 100644 index f2030df9d..000000000 --- a/packages/effect-app/src/Store.ts +++ /dev/null @@ -1,325 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Redacted from "effect/Redacted" -import * as Semaphore from "effect/Semaphore" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import type { DatabaseError, OptimisticConcurrencyException } from "./client/errors.ts" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import type { FilterResult } from "./Model/filter/filterApi.ts" -import type { FieldValues } from "./Model/filter/types.ts" -import type { FieldPath } from "./Model/filter/types/path/index.ts" -import type { AggregateIrExpression, ComputedProjectionIrExpression, RawQuery } from "./Model/query.ts" -import type * as Option from "./Option.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import { type Json as SchemaJson, NonEmptyString255, type SchemaError, type Top as SchemaTop } from "./Schema.ts" - -/** - * Adapter-neutral unique-key definition for stores that support unique indexes, - * such as the Cosmos adapter. This shape is intentionally kept structurally - * compatible with adapter-specific `UniqueKey` types. Each path identifies a - * field participating in the unique key, and adapters forward these paths - * directly to the underlying storage engine. - */ -export interface UniqueKey { - readonly paths: string[] -} - -/** - * A raw stored JSON document: a readonly record of JSON values. - * - * This is the shape a document has *in the database*: dates are ISO strings, - * `ReadonlyMap`/`ReadonlySet` are arrays, and absent or legacy keys are - * whatever was written. It is deliberately not the `Encoded` shape, which - * holds native `Date`/`Map`/`Set` values. - */ -export type JsonRecord = { readonly [key: string]: SchemaJson } - -export interface StoreConfig { - partitionValue: (e?: E) => string - /** - * Primarily used for testing, creating namespaces in the database to separate data e.g to run multiple tests in isolation within the same database. - * Memory/Disk use separate store instances per namespace. CosmosDB uses namespace-prefixed partition keys. SQL uses a `_namespace` column. - */ - allowNamespace?: (namespace: string) => boolean - /** - * just in time migrations, supported by the database driver, supporting queries, for simple default values - */ - defaultValues?: Partial - - /** - * just in time Migration: for complex migrations that aren't just simple - * default values - use {@link defaultValues} for those. - * - * Runs at the store boundary on the **raw JSON document**: after - * {@link defaultValues} have been merged in (they still only fill *absent* - * keys) and *before* the JSON→Encoded decode - * (`Schema.toCodecJson(Schema.toEncoded(schema))`). - * - * It therefore sees exactly what is stored - a `Date` field as an ISO - * string, a `ReadonlyMap` as an array of pairs, an explicit `null` as - * `null` - and it must return JSON again, never native Encoded values - * (no `Date`/`Map`/`Set` instances). That is what lets it repair legacy - * shapes, including explicit `null`s, before any schema decode happens. - * - * `_etag` is infrastructure metadata and is not part of the document passed - * to `jitM`. Not applied on the write/encode path. - */ - jitM?: (json: JsonRecord) => JsonRecord - - /** - * How many items can be processed in one batch at a time. - * Defaults to 100 for CosmosDB. - */ - maxBulkSize?: number - - /** - * Unique indexes, mainly for CosmosDB - */ - uniqueKeys?: UniqueKey[] - /** - * Domain schema whose Encoded shape is stored. JSON adapters use - * `Schema.toCodecJson(Schema.toEncoded(schema))` for documents, query - * values, and defaults. Date/Map/Set (and app types such as DateOnly) - * stay native on Encoded and lower only on this JSON boundary. - */ - schema?: SchemaTop -} - -export type SupportedValues = string | boolean | number | null -export type SupportedValues2 = string | boolean | number - -// default is eq -export type Where = - | { key: string; t?: "eq" | "not-eq"; value: SupportedValues } - | { key: string; t: "gt" | "lt" | "gte" | "lte"; value: SupportedValues2 } - | { - key: string - t: "contains" | "starts-with" | "ends-with" | "not-contains" | "not-starts-with" | "not-ends-with" - value: string - } - | { key: string; t: "includes" | "not-includes"; value: string } - | { - key: string - t: "in" | "not-in" - value: readonly (SupportedValues)[] - } - -export type Filter = readonly FilterResult[] - -export interface O { - key: FieldPath - direction: "ASC" | "DESC" -} - -export interface FilterArgs { - t: Encoded - filter?: Filter | undefined - select?: - | NonEmptyReadonlyArray< - U | { key: string; subKeys: readonly string[] } | { - key: string - computed: ComputedProjectionIrExpression - } | { - key: string - path: string - } | { - key: string - aggregate: AggregateIrExpression - } - > - | undefined - order?: NonEmptyReadonlyArray>> - limit?: number | undefined - skip?: number | undefined -} - -/** decodes stored documents unless `select` projects fields, hence `SchemaError` */ -export type FilterFunc = ( - args: FilterArgs -) => Effect.Effect<(U extends undefined ? Encoded : Pick)[], DatabaseError | SchemaError> - -export interface Store< - IdKey extends keyof Encoded, - Encoded extends FieldValues, - PM extends PersistenceModelType = PersistenceModelType -> { - /** decodes every stored document, so a document `jitM` does not repair fails with `SchemaError` */ - all: Effect.Effect - filter: FilterFunc - /** decodes the stored document, so a document `jitM` does not repair fails with `SchemaError` */ - find: (id: Encoded[IdKey]) => Effect.Effect, DatabaseError | SchemaError> - /** adapters that decode the written document on the way back can fail with `SchemaError` */ - set: (e: PM) => Effect.Effect - /** adapters that decode the written documents on the way back can fail with `SchemaError` */ - batchSet: ( - items: NonEmptyReadonlyArray - ) => Effect.Effect, OptimisticConcurrencyException | DatabaseError | SchemaError> - /** adapters that decode the written documents on the way back can fail with `SchemaError` */ - bulkSet: ( - items: NonEmptyReadonlyArray - ) => Effect.Effect, OptimisticConcurrencyException | DatabaseError | SchemaError> - batchRemove: (ids: NonEmptyReadonlyArray, partitionKey?: string) => Effect.Effect - /** - * Runs the raw query over stored JSON documents, not Encoded. - * `Repository.queryRaw` applies `toCodecJson` to the projector output. - */ - queryRaw: (query: RawQuery) => Effect.Effect - /** - * Explicitly seed a namespace. Primary is seeded eagerly on initialization. - * Non-primary namespaces must be seeded explicitly before use. - */ - seedNamespace: (namespace: string) => Effect.Effect -} - -export class StoreMaker extends Context.Opaque( - name: string, - idKey: IdKey, - seed?: Effect.Effect, E, R>, - config?: StoreConfig - ) => Effect.Effect, E, R> -}>()("effect-app/StoreMaker") { -} - -export const makeContextMap = () => { - const etags = new Map() - const getEtag = (id: string) => etags.get(id) - const setEtag = (id: string, eTag: string | undefined) => { - if (eTag === undefined) { - etags.delete(id) - } else { - etags.set(id, eTag) - } - } - - // const parsedCache = new Map< - // Parser, - // Map> - // >() - - // const parserCache = new Map< - // Parser, - // (i: any) => These.These - // >() - - // const setAndReturn = ( - // p: Parser, - // np: (i: I) => These.These - // ) => { - // parserCache.set(p, np) - // return np - // } - - // const parserEnv: ParserEnv = { - // // TODO: lax: true would turn off refinement checks, may help on large payloads - // // but of course removes confirming of validation rules (which may be okay for a database owned by the app, as we write safely) - // lax: false, - // cache: { - // getOrSetParser: (p) => parserCache.get(p) ?? setAndReturn(p, (i) => parserEnv.cache!.getOrSet(i, p)), - // getOrSetParsers: (parsers) => { - // return Object.entries(parsers).reduce((prev, [k, v]) => { - // prev[k] = parserEnv.cache!.getOrSetParser(v) - // return prev - // }, {} as any) - // }, - // getOrSet: (i, parse): any => { - // const c = parsedCache.get(parse) - // if (c) { - // const f = c.get(i) - // if (f) { - // // console.log("$$$ cache hit", i) - // return f - // } else { - // const nf = parse(i, parserEnv) - // c.set(i, nf) - // return nf - // } - // } else { - // const nf = parse(i, parserEnv) - // parsedCache.set(parse, new Map([[i, nf]])) - // return nf - // } - // } - // } - // } - - const store = new Map() - const sem = Semaphore.makeUnsafe(1) - - return { - createdAt: new Date(), - get: getEtag, - set: setEtag, - getOrCreateStore: (key: symbol, make: () => T): T => { - let value = store.get(key) as T | undefined - if (value === undefined) { - value = make() - store.set(key, value) - } - return value - }, - getOrCreateStoreEffect: (key: symbol, make: Effect.Effect): Effect.Effect => - sem.withPermits(1)(Effect.uninterruptible(Effect.gen(function*() { - const value = store.get(key) as T | undefined - if (value !== undefined) return value - const v = yield* make - store.set(key, v) - return v - }))), - clear: () => { - etags.clear() - store.clear() - } - } -} - -const makeMap = Effect.acquireRelease( - Effect.sync(() => makeContextMap()), - (m) => Effect.sync(() => m.clear()) -) - -export class ContextMap extends Context.Opaque()("effect-app/ContextMap", { make: makeMap }) { -} - -export const ContextMapContainer = RequestScopedDependencies.make( - "ContextMapContainer", - ContextMap.make.pipe(Effect.map(ContextMap.of)) -) - -export type ContextMapNotStartedError = RequestScopedDependencies.RequestScopedDependencyNotStartedError - -export const getContextMap = ContextMapContainer.current - -/** - * Runs `make` at most once per ContextMap (i.e. per request) and caches the - * resulting value in the ContextMap under a fresh symbol. Subsequent calls of - * the returned Effect within the same ContextMap return the cached value. - * - * Uses the ContextMap's shared semaphore for safe single initialization. - */ -export const cachedPerRequest = ( - make: Effect.Effect -): Effect.Effect => { - const cacheKey = Symbol() - return getContextMap.pipe( - Effect.flatMap((ctxMap) => ctxMap.getOrCreateStoreEffect(cacheKey, make)) - ) -} - -const defaultNs: NonEmptyString255 = NonEmptyString255("primary") -export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {} - -export type PersistenceModelType = Encoded & { - _etag?: string | undefined -} - -export interface StorageConfig { - url: Redacted.Redacted - prefix: string - dbName: string - /** - * Cosmos only: autoscale max RU/s for containers the store creates, in databases - * without shared throughput. Unset keeps the Cosmos default (manual 400 RU/s). - */ - autoscaleMaxThroughput?: number | undefined -} diff --git a/packages/effect-app/src/TypeTest.ts b/packages/effect-app/src/TypeTest.ts deleted file mode 100644 index d7b7018db..000000000 --- a/packages/effect-app/src/TypeTest.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Nice way to underline types that are only there for type testing, not for production use -// sadly with unique symbols we get weird issues in app projects. -// api/src/X/PackList.Controllers.ts:21:1 - error TS4082: Default export of the module has or is using private name 'TypeTestId' -// export const TypeTestId: unique symbol = Symbol.for("@effect/infra/type-test") -// export type TypeTestId = typeof TypeTestId -export const TypeTestId = "effect-app/type-test" as const -export type TypeTestId = typeof TypeTestId diff --git a/packages/effect-app/src/Types.ts b/packages/effect-app/src/Types.ts deleted file mode 100644 index 99bc8370d..000000000 --- a/packages/effect-app/src/Types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export * from "effect/Types" - -export type DeepMutable = T extends ReadonlyMap ? Map, DeepMutable> - : T extends ReadonlySet ? Set> - : [keyof T] extends [never] ? T - // keep brands alive - : T extends string ? T - : T extends number ? T - : { -readonly [K in keyof T]: DeepMutable } - -export type Mutable = { - -readonly [P in keyof T]: T[P] -} - -export type DistributiveOmit = T extends any ? Omit - : never diff --git a/packages/effect-app/src/Widen.type.ts b/packages/effect-app/src/Widen.type.ts deleted file mode 100644 index 6ad23d875..000000000 --- a/packages/effect-app/src/Widen.type.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { BuiltInObject } from "effect-app/utils" - -type AllKeys = T extends any ? keyof T : never - -type OptionalKeys = T extends any ? { [K in keyof T]-?: {} extends Pick ? K : never }[keyof T] - : never - -type Idx = T extends any ? K extends keyof T ? T[K] - : D - : never - -type PartialKeys = - & Omit - & Partial> extends infer O ? { [P in keyof O]: O[P] } - : never - -export type Primitive = boolean | string | number | bigint | symbol | undefined | null -export type Widen = [T] extends [BuiltInObject] | [Primitive] ? T - : [T] extends [Array] ? { [K in keyof T]: Widen } - : [T] extends [ReadonlyArray] ? { [K in keyof T]: Widen } - : [T] extends [object] ? PartialKeys< - { [K in AllKeys]: Widen> }, - Exclude, keyof T> | OptionalKeys - > - : T diff --git a/packages/effect-app/src/_ext/Array.ts b/packages/effect-app/src/_ext/Array.ts deleted file mode 100644 index 27f2ad0fa..000000000 --- a/packages/effect-app/src/_ext/Array.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as Chunk from "effect-app/Chunk" -import * as Effect from "effect-app/Effect" -import * as Equal from "effect/Equal" -import { NotFoundError } from "../client/errors.ts" - -function getFirstBy( - a: Iterable, - idKey: keyof A, - id: A[typeof idKey], - type: Type -) { - return Effect - .fromOption( - Chunk.fromIterable(a).pipe(Chunk.findFirst((_) => Equal.equals(_[idKey], id))) - ) - .pipe(Effect.mapError(() => new NotFoundError({ type, id }))) -} - -export function makeGetFirstBy() { - return (idKey: Id, type: Type) => - ( - a: Iterable, - id: A[Id] - ) => getFirstBy(a, idKey, id, type) -} - -export const makeGetFirstById = () => (type: Type) => - makeGetFirstBy()("id", type) - -export function getFirstById( - a: Iterable, - id: A["id"], - type: Type -) { - return getFirstBy(a, "id", id, type) -} diff --git a/packages/effect-app/src/_ext/date.ts b/packages/effect-app/src/_ext/date.ts deleted file mode 100644 index 60169bea5..000000000 --- a/packages/effect-app/src/_ext/date.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { addDays, addHours, addMinutes, addMonths, addSeconds, addWeeks, addYears, subDays, subHours, subMinutes, subMonths, subSeconds, subWeeks, subYears } from "date-fns" - -export const DateAddDays: (date: Date, amount: number) => Date = addDays -export const DateSubDays: (date: Date, amount: number) => Date = subDays - -export const DateAddHours: (date: Date, amount: number) => Date = addHours -export const DateSubHours: (date: Date, amount: number) => Date = subHours - -export const DateAddMinutes: (date: Date, amount: number) => Date = addMinutes - -export const DateSubMinutes: (date: Date, amount: number) => Date = subMinutes - -export const DateAddSeconds: (date: Date, amount: number) => Date = addSeconds - -export const DateSubSeconds: (date: Date, amount: number) => Date = subSeconds - -export const DateAddYears: (date: Date, amount: number) => Date = addYears - -export const DateSubYears: (date: Date, amount: number) => Date = subYears - -export const DateAddMonths: (date: Date, amount: number) => Date = addMonths - -export const DateSubMonths: (date: Date, amount: number) => Date = subMonths - -export const DateAddWeeks: (date: Date, amount: number) => Date = addWeeks - -export const DateSubWeeks: (date: Date, amount: number) => Date = subWeeks diff --git a/packages/effect-app/src/_ext/misc.ts b/packages/effect-app/src/_ext/misc.ts deleted file mode 100644 index 0d4c2a736..000000000 --- a/packages/effect-app/src/_ext/misc.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { LazyArg } from "effect-app/Function" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Result from "effect/Result" -import type * as Scope from "effect/Scope" - -export type _R> = [T] extends [ - Effect.Effect -] ? R - : never - -export type _E> = [T] extends [ - Effect.Effect -] ? E - : never - -export function encaseMaybeInEffect_( - o: Option.Option, - onError: LazyArg -): Effect.Effect { - return Option.match(o, { onNone: () => Effect.fail(onError()), onSome: Effect.succeed }) -} - -export function encaseMaybeEither_( - o: Option.Option, - onError: LazyArg -): Result.Result { - return Option.match(o, { onNone: () => Result.fail(onError()), onSome: Result.succeed }) -} - -export function toNullable( - self: Effect.Effect, E, R> -) { - return Effect.map(self, (_) => Option.getOrNull(_)) -} - -export function scope( - scopedEffect: Effect.Effect, - effect: Effect.Effect -): Effect.Effect> { - return Effect.andThen(scopedEffect, effect).pipe(Effect.scoped) -} - -export function flatMapScoped( - scopedEffect: Effect.Effect, - effect: (a: A) => Effect.Effect -): Effect.Effect> { - return scopedEffect.pipe(Effect.flatMap(effect), Effect.scoped) -} diff --git a/packages/effect-app/src/_ext/ord.ext.ts b/packages/effect-app/src/_ext/ord.ext.ts deleted file mode 100644 index b89c2c949..000000000 --- a/packages/effect-app/src/_ext/ord.ext.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as Chunk from "effect/Chunk" -import type * as Equivalence from "effect/Equivalence" - -export function toArray( - gen: Generator -) { - return Array.from(gen) -} - -/** - * Remove duplicates from an array, keeping the first occurrence of an element. - */ -export function uniq(E: Equivalence.Equivalence) { - return (self: Chunk.Chunk): Chunk.Chunk => { - let out = Chunk.fromIterable([]) - for (let i = 0; i < self.length; i++) { - const a = Chunk.getUnsafe(self, i) - if (!elem(E, a)(out)) { - out = Chunk.append(out, a) - } - } - return self.length === out.length ? self : out - } -} - -/** - * Test if a value is a member of an array. Takes a `Equivalence.Equivalence` as a single - * argument which returns the function to use to search for a value of type `A` - * in an array of type `Chunk.Chunk`. - */ -export function elem(E: Equivalence.Equivalence, value: A) { - return (self: Chunk.Chunk): boolean => { - for (let i = 0; i < self.length; i++) { - if (E(Chunk.getUnsafe(self, i), value)) { - return true - } - } - return false - } -} diff --git a/packages/effect-app/src/builtin-json.d.ts b/packages/effect-app/src/builtin-json.d.ts deleted file mode 100644 index 1e643f362..000000000 --- a/packages/effect-app/src/builtin-json.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -// JSON / Body return-type overrides (`any` -> `unknown`), isolated from -// `builtin.ts` so consumers that source-link this package (e.g. an embedded -// Effect-source setup) can toggle them off. As a `declare global`, the override -// is program-wide: in linked mode it leaks into source-linked Effect and breaks -// Effect's own `JSON.parse(...)` / `response.json()` call sites (TS2322 -// `unknown` vs `Json`). `builtin.ts` triple-slash-references this file, so -// normal builds keep the override. -declare global { - interface JSON { - /** - * Converts a JavaScript Object Notation (JSON) string into an object. - * @param text A valid JSON string. - * @param reviver A function that transforms the results. This function is called for each member of the object. - * If a member contains nested objects, the nested objects are transformed before the parent object is. - */ - parse(text: string, reviver?: (this: any, key: string, value: any) => any): unknown - } - - interface Body { - json(): Promise - } -} - -export {} diff --git a/packages/effect-app/src/builtin.ts b/packages/effect-app/src/builtin.ts deleted file mode 100644 index 084a6ce79..000000000 --- a/packages/effect-app/src/builtin.ts +++ /dev/null @@ -1,53 +0,0 @@ -// oxlint-disable-next-line typescript/triple-slash-reference -/// -import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect/Array" - -declare global { - interface String { - /** - * Split a string into substrings using the specified separator and return them as an array. - * @param splitter An object that can split a string. - * @param limit A value used to limit the number of elements returned in the array. - */ - split(splitter: { [Symbol.split](string: string, limit?: number): string[] }, limit?: number): [string, ...string[]] - - /** - * Split a string into substrings using the specified separator and return them as an array. - * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. - * @param limit A value used to limit the number of elements returned in the array. - */ - split(separator: string | RegExp, limit?: number): [string, ...string[]] - } - - // JSON.parse / Body.json overrides live in ./builtin-json.d.ts (referenced - // above) so a source-linked consumer can disable them in linked mode. - - interface Array { - // Conditional return instead of a `this`-typed overload: a `this` overload - // is selected-then-rejected on union-of-array receivers (TS2684), poisoning - // foreign code like `(readonly A[] | readonly B[]).map(...)`. A single - // conditional-return signature refines NonEmpty without a `this` param. - map( - callbackfn: (value: T, index: number, array: T[]) => U, - thisArg?: any - ): this extends NonEmptyArray ? NonEmptyArray : U[] - } - interface ReadonlyArray { - // Subsequent property declarations must have the same type. Property 'length' must be of type 'number', but here has type 'NonNegativeInt'. - // readonly length: NonNegativeInt - - map( - callbackfn: (value: T, index: number, array: readonly T[]) => U, - thisArg?: any - ): this extends NonEmptyReadonlyArray ? NonEmptyReadonlyArray : U[] - } -} - -declare module "effect/Option" { - export interface None { - get value(): A | undefined - } -} - -// TODO: v4 migration — Either module augmentation removed (Either → Result) -// Previously added .right to Left and .left to Right for convenience access diff --git a/packages/effect-app/src/client.ts b/packages/effect-app/src/client.ts deleted file mode 100644 index 578b82dd7..000000000 --- a/packages/effect-app/src/client.ts +++ /dev/null @@ -1,8 +0,0 @@ -// codegen:start {preset: barrel, include: ./client/*.ts} -export * from "./client/apiClientFactory.ts" -export * from "./client/clientFor.ts" -export * from "./client/errors.ts" -export * from "./client/InvalidationKeys.ts" -export * from "./client/makeClient.ts" -// codegen:end -export * as DataDependencies from "./DataDependencies.ts" diff --git a/packages/effect-app/src/client/InvalidationKeys.ts b/packages/effect-app/src/client/InvalidationKeys.ts deleted file mode 100644 index 6e0086335..000000000 --- a/packages/effect-app/src/client/InvalidationKeys.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as Equal from "effect/Equal" -import * as Ref from "effect/Ref" -import * as Context from "../Context.ts" -import * as Effect from "../Effect.ts" -import type { InvalidationKey } from "../rpc/Invalidation.ts" - -export type { InvalidationKey } -/** Shape of the per-mutation service that accumulates server-provided invalidation keys. */ -export interface InvalidationKeysService { - readonly add: (key: InvalidationKey) => Effect.Effect - readonly get: Effect.Effect> -} - -/** - * Context.Reference that accumulates invalidation keys received from the server via the - * `x-invalidate` HTTP response header. - * - * The default is a no-op: when not explicitly provided (e.g. outside a mutation wrapper) - * all calls are ignored. The mutation wrapper in `@effect-app/vue` provides a real - * implementation backed by a `Ref`. - */ -export const InvalidationKeysFromServer = Context.Reference( - "effect-app/client/InvalidationKeysFromServer", - { - defaultValue: () => ({ - add: (_key: InvalidationKey) => Effect.void, - get: Effect.succeed([] as ReadonlyArray) - }) - } -) -export type InvalidationKeysFromServer = typeof InvalidationKeysFromServer - -/** - * Creates a fresh `InvalidationKeysService` implementation backed by a `Ref`. - * - * @param ref - The `Ref` that stores the accumulated keys. - * @param onAdded - V3: Optional Effect run after a distinct key is added. Use to trigger - * mid-stream query invalidation without waiting for the stream to complete. Repeated keys - * stay accumulated for the final refresh but do not retrigger the callback. - */ -export const makeInvalidationKeysService = ( - ref: Ref.Ref>, - onAdded?: (key: InvalidationKey) => Effect.Effect -): InvalidationKeysService => ({ - add: (key) => - Ref - .modify(ref, (keys) => { - if (keys.some((existing) => Equal.equals(existing, key))) return [false, keys] - return [true, [...keys, key]] - }) - .pipe( - Effect.flatMap((added) => added && onAdded ? onAdded(key) : Effect.void) - ), - get: Ref.get(ref) -}) diff --git a/packages/effect-app/src/client/apiClientFactory.ts b/packages/effect-app/src/client/apiClientFactory.ts deleted file mode 100644 index c55d8d562..000000000 --- a/packages/effect-app/src/client/apiClientFactory.ts +++ /dev/null @@ -1,464 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { constant, flow } from "effect/Function" -import * as Layer from "effect/Layer" -import * as ManagedRuntime from "effect/ManagedRuntime" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/rpc" -import * as Schema from "effect/Schema" -import * as Stream from "effect/Stream" -import * as Struct from "effect/Struct" -import * as Config from "../Config.ts" -import * as Context from "../Context.ts" -import * as DataDependencies from "../DataDependencies.ts" -import * as Effect from "../Effect.ts" -import { HttpClient, HttpClientRequest } from "../http.ts" -import { Invalidation } from "../rpc.ts" -import type * as S from "../Schema.ts" -import { typedKeysOf, typedValuesOf } from "../utils.ts" -import type { Client, ClientForOptions, ExtractModuleName, RequestsAny } from "./clientFor.ts" -import { InvalidationKeysFromServer } from "./InvalidationKeys.ts" - -export interface ApiConfig { - url: string - headers: Option.Option> -} - -export const DefaultApiConfig = Config.all({ - url: Config.String("apiUrl").pipe(Config.withDefault("/api")), - headers: Config.Record(Schema.String, Schema.String, "headers").pipe(Config.option) -}) - -export type Req = S.Top & { - readonly make: (...args: any[]) => any - _tag: string - fields: S.Struct.Fields - success: S.Top - error: S.Top - /** Optional final-value schema for stream requests. When set, the execute effect resolves with the last stream value decoded to this type. */ - final?: S.Top - config?: Record - readonly id: string - readonly moduleName: string - readonly type: "command" | "query" - readonly stream: boolean - readonly middleware?: unknown -} - -class RequestName extends Context.Reference("RequestName", { - defaultValue: () => ({ requestName: "Unspecified", moduleName: "Error" }) -}) {} - -export const HttpClientLayer = (config: ApiConfig) => - Layer.effect( - HttpClient.HttpClient, - Effect - .gen(function*() { - const baseClient = yield* HttpClient.HttpClient - const client = baseClient.pipe( - HttpClient.mapRequest(HttpClientRequest.prependUrl(config.url + "/rpc")), - HttpClient.mapRequest( - HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() => ({})))) - ), - HttpClient.mapRequestEffect((req) => - Effect.map(RequestName, (ctx) => - flow( - HttpClientRequest.appendUrlParam("action", ctx.requestName), - HttpClientRequest.appendUrl("/" + ctx.moduleName) - )(req)) - ) - ) - return client - }) - ) - -export const HttpClientFromConfigLayer = Layer.unwrap( - Effect.gen(function*() { - const config = yield* DefaultApiConfig - return HttpClientLayer(config) - }) -) - -export const RpcSerializationLayer = (config: ApiConfig) => - Layer.mergeAll( - RpcSerialization.layerNdjson, - HttpClientLayer(config) - ) - -type RpcHandlers = { - [K in keyof M]: Rpc.Rpc -} - -const getFiltered = (resource: M) => { - type Filtered = { - [K in keyof M as M[K] extends Req ? K : never]: M[K] extends Req ? M[K] : never - } - // TODO: Record.filter - const filtered = typedKeysOf(resource).reduce((acc, cur) => { - if ( - Predicate.isObjectKeyword(resource[cur]) - && (resource[cur].success) - ) { - acc[cur as keyof Filtered] = resource[cur] as any - } - return acc - }, {} as Record) - - return filtered as unknown as Filtered -} - -export const getMeta = ( - resource: M -): { moduleName: ExtractModuleName; middleware?: unknown } => { - const first = typedValuesOf(getFiltered(resource))[0] - if (first && "moduleName" in first) return { moduleName: first.moduleName, middleware: (first as any).middleware } - throw new Error("No moduleName on requests!") -} - -export const makeRpcGroupFromRequestsAndModuleName = ( - resource: M, - moduleName: ModuleName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - middleware?: any -) => { - const filtered = getFiltered(resource) - type newM = typeof filtered - const baseRpcs = RpcGroup - .make( - ...typedValuesOf(filtered).map((_) => { - const r = _ as any - const isStream = r.stream - const isCommand = r.type === "command" - return (isCommand - ? isStream - ? Invalidation.makeStreamRpc(r._tag, { - payload: r, - success: r.success, - error: r.error, - stream: true as const - }) - : Invalidation.makeCommandRpc(r._tag, { payload: r, success: r.success, error: r.error }) - : Rpc.make(r._tag, { - payload: r, - success: isStream ? r.success : Invalidation.QueryResponseWithMetaData(r.success), - error: r.error, - stream: isStream - })) as any - }) - ) - .prefix(`${moduleName}.`) - // Attach the middleware tag (schema-only on the client — no Live invoked) - // so its declared `error` joins the rpc failure union via - // `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. Required for stream - // rpcs whose top-level `errorSchema` is forced to `Never` by effect-rpc; - // without it, middleware-thrown errors fail client decode. - const rpcs = (middleware ? baseRpcs.middleware(middleware) : baseRpcs) as unknown as RpcGroup.RpcGroup< - Rpc.Prefixed[keyof newM], `${ModuleName}.`> - > - return rpcs -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const makeRpcTag = (resource: M, middleware?: any) => { - const meta = getMeta(resource) - const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName, middleware) - - // Use Object.assign instead of class extension to avoid TS2509 with complex generic return types. - // The first type arg is `any` because this is a dynamically created tag — its identity is the string key. - const TheClient = Context.Opaque< - any, - RpcClient.RpcClient> - >()(`RpcClient.${meta.moduleName}`) - // Use Layer.effect directly (not TheClient.toLayer) so TypeScript properly excludes Scope - const layer = Layer.effect( - TheClient, - RpcClient.make(rpcs) - ) - return Object.assign(TheClient, { layer }) -} - -const makeApiClientFactory = Effect - .gen(function*() { - const ctx = yield* Effect.context() - const makeClientFor = Effect.fnUntraced(function*( - resource: M, - requestLevelLayers = Layer.empty, - options?: ClientForOptions - ) { - const meta = getMeta(resource) - - const TheClient = makeRpcTag(resource, meta.middleware) - - // TODO: somehow we need a protocol per REQUEST kind of it seems ... - // otherwise it locks up on the client, navigation remains empty... - const clientLayer = TheClient.layer.pipe( - // add ApiClientFactory for nested schemas - // eslint-disable-next-line @typescript-eslint/no-use-before-define - Layer.provide(Layer.succeed(ApiClientFactory, makeClientForCached as any)), - Layer.provide( - RpcClient - .layerProtocolHttp({ url: "" }) // why not here set meta.moduleName as root? - .pipe( - Layer.provideMerge(Layer.succeedContext(ctx)) - ) - ) - ) - const mr = ManagedRuntime.make(clientLayer) - - const filtered = getFiltered(resource) - - // Unwrap `CommandResponseWithMetaData` (success) and `CommandFailureWithMetaData` - // (handler-thrown failure): forward accumulated invalidation keys to - // `InvalidationKeysFromServer` and yield the raw payload / re-fail with the raw - // error. Middleware-thrown failures arrive raw on the Cause already (no wrap to - // strip) — the `else` branch passes them through. - const forwardDependencies = ( - dependencies: DataDependencies.DataDependencySet | undefined, - options?: { readonly reads?: boolean } - ) => - dependencies === undefined - ? Effect.void - : Effect.gen(function*() { - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - if (options?.reads) { - yield* Effect.forEach(dependencies.reads, dependencyRecorder.read, { discard: true }) - } - yield* Effect.forEach(dependencies.writes, dependencyRecorder.write, { discard: true }) - }) - - const unwrapCommand = (eff: Effect.Effect): Effect.Effect => - eff.pipe( - Effect.flatMap((result: any) => - Effect.gen(function*() { - const keys: ReadonlyArray = result?.metadata?.invalidateQueries ?? [] - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true }) - yield* forwardDependencies(result?.metadata?.dataDependencies) - return result.payload - }) - ), - Effect.catch((result: any) => - result?._tag === "CommandFailureWithMetaData" - ? Effect.gen(function*() { - const keys: ReadonlyArray = result.metadata?.invalidateQueries ?? [] - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true }) - yield* forwardDependencies(result.metadata?.dataDependencies) - return yield* Effect.fail(result.error) - }) - : Effect.fail(result) - ) - ) - - const unwrapQuery = (eff: Effect.Effect): Effect.Effect => - eff.pipe( - Effect.flatMap((result: any) => - Effect.gen(function*() { - yield* forwardDependencies(result?.metadata?.dataDependencies, { reads: true }) - return result.payload - }) - ) - ) - - return { - mr, - client: typedKeysOf(filtered) - .reduce((prev, cur) => { - const h = filtered[cur]! - - const Request = h - - const id = `${meta.moduleName}.${cur as string}` - .replaceAll(".js", "") - - const requestMeta = { - Request, - id, - options, - disableQueryInvalidation: (h as { config?: { disableQueryInvalidation?: boolean } }) - .config - ?.disableQueryInvalidation === true - } - - const requestNameLayer = Layer.succeed(RequestName, { - requestName: cur as string, - moduleName: meta.moduleName - }) - - const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer)) - - const fields = Struct.omit(Request.fields, ["_tag"] as const) - const requestAttr = `${meta.moduleName}.${h._tag}` - const isCommand = h.type === "command" - const isStream = h.stream - - const buildEffect = (input: any) => - mr.contextEffect.pipe( - Effect.flatMap((svcs) => { - const rpcEffect = TheClient - .use((client) => (client as any)[requestAttr]!(Request.make(input)) as Effect.Effect) - .pipe( - // local: true → isolate to this RPC call. `layers` is pure today - // (RequestName + caller-supplied requestLevelLayers), but the - // flag prevents a stateful caller-supplied layer from leaking - // across RPC calls via the ambient fiber MemoMap. - Effect.provide(layers, { local: true }), - Effect.provide(svcs) - ) - return isCommand ? unwrapCommand(rpcEffect) : isStream ? rpcEffect : unwrapQuery(rpcEffect) - }), - // Forwarding the server's dependency metadata needs a recorder in scope. A parent - // query/command (vue) provides one; a top-level call gets a fresh per-request one. - DataDependencies.ensureDataDependencyRecorder - ) - - const buildStream = (input: any) => { - const stream = Stream.unwrap( - mr.contextEffect.pipe( - Effect.flatMap((svcs) => - TheClient - .useSync((client) => { - const rpcStream = (client as any)[requestAttr]!( - Request.make(input) - ) as Stream.Stream - return rpcStream.pipe( - // Collect server invalidation keys from the "done" chunk, then discard it. - Stream.tap((item: any) => - item._tag === "done" || item._tag === "metadata" - ? Effect.gen(function*() { - const metadata = item.metadata as Invalidation.CommandMetaData - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(metadata.invalidateQueries, invalidationKeys.add, { - discard: true - }) - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - yield* Effect.forEach( - metadata.dataDependencies.reads, - dependencyRecorder.read, - { discard: true } - ) - yield* Effect.forEach( - metadata.dataDependencies.writes, - dependencyRecorder.write, - { discard: true } - ) - }) - : Effect.void - ), - Stream.filter((item: any) => item._tag === "value"), - Stream.map((item: any) => item.value), - // V2: unwrap StreamFailureChunk — forward keys from failures too, - // then re-fail with the original error so callers see the unmodified - // error type. - Stream.catch((err: any) => - err?._tag === "error" && err?.metadata - ? Stream.fromEffect( - Effect.gen(function*() { - const metadata = err.metadata as Invalidation.CommandMetaData - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(metadata.invalidateQueries, invalidationKeys.add, { - discard: true - }) - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - yield* Effect.forEach( - metadata.dataDependencies.writes, - dependencyRecorder.write, - { discard: true } - ) - return yield* Effect.fail(err.error) - }) - ) - : Stream.fail(err) - ), - // local: true — see buildEffect above. - Stream.provide(layers, { local: true }), - Stream.provide(svcs) - ) - }) - .pipe(Effect.provide(svcs)) - ) - ) - ) - // Forwarding the server's dependency metadata needs a recorder in scope. A parent - // query/command (vue) provides one; a top-level stream call gets a fresh per-request one. - return Stream.unwrap( - Effect.map( - DataDependencies.DataDependencyRecorder, - (current) => - current === "root" - ? Stream.provide(stream, DataDependencies.DataDependencyRecorder.layer) - : stream - ) - ) - } - - // @ts-expect-error doc - prev[cur] = Object.keys(fields).length === 0 - ? { - handler: isStream ? constant(buildStream({})) : constant(buildEffect({})), - ...requestMeta - } - : { - handler: isStream - ? (req: any) => buildStream(req) - : (req: any) => buildEffect(req), - ...requestMeta - } - - return prev - }, {} as Client>) - } - }) - - const register: ManagedRuntime.ManagedRuntime[] = [] - yield* Effect.addFinalizer(() => Effect.forEach(register, (mr) => mr.disposeEffect)) - - const cacheL = new Map>>() - - function makeClientForCached(requestLevelLayers: Layer.Layer, options?: ClientForOptions) { - let cache = cacheL.get(requestLevelLayers) - if (!cache) { - cache = new Map>() - cacheL.set(requestLevelLayers, cache) - } - - return Effect.fnUntraced(function*(models: M) { - const found = cache.get(models) as Client> | undefined - if (found) { - return found - } - const m = yield* makeClientFor(models, requestLevelLayers, options) - cache.set(models, m.client) - register.push(m.mr) - return m.client - }) - } - - return makeClientForCached - }) - -/** - * Used to create clients for resource modules. - */ -export class ApiClientFactory - extends Context.Opaque>()("ApiClientFactory") -{ - static readonly layer = (config: ApiConfig) => - ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config))) - static readonly layerFromConfig = Layer.unwrap( - Effect.gen(function*() { - const config = yield* DefaultApiConfig - return ApiClientFactory.layer(config) - }) - ) - - static readonly makeFor = - (requestLevelLayers: Layer.Layer, options?: ClientForOptions) => - ( - resource: M - ) => - ApiClientFactory.use((apiClientFactory) => { - const f = apiClientFactory(requestLevelLayers, options) - return f(resource) - }) -} diff --git a/packages/effect-app/src/client/clientFor.ts b/packages/effect-app/src/client/clientFor.ts deleted file mode 100644 index bb4da478e..000000000 --- a/packages/effect-app/src/client/clientFor.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Record from "effect/Record" -import type * as Stream from "effect/Stream" -import type { Path } from "path-parser" -import qs from "query-string" -import type * as Effect from "../Effect.ts" -import type * as S from "../Schema.ts" -import { type Req } from "./apiClientFactory.ts" - -export function makePathWithQuery( - path: Path, - pars: Record< - string, - | string - | number - | boolean - | readonly string[] - | readonly number[] - | readonly boolean[] - | null - > -) { - const forQs = Record.filter(pars, (_, k) => !path.params.includes(k)) - const q = forQs // { ...forQs, _: JSON.stringify(forQs) } // TODO: drop completely individual keys from query?, sticking to json only - return ( - path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) - + (Object.keys(q).length - ? "?" + qs.stringify(q) - : "") - ) -} - -export function makePathWithBody( - path: Path, - pars: Record< - string, - | string - | number - | boolean - | readonly string[] - | readonly number[] - | readonly boolean[] - | null - > -) { - return path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) -} - -export type Requests = RequestsAny -export type RequestsAny = Record - -export type ExtractModuleName = - { [K in keyof M]: M[K] extends { moduleName: infer N extends string } ? N : never }[keyof M] extends - infer R extends string ? R - : string - -export type Client = RequestHandlers< - never, - never, - M, - ModuleName -> - -export type ExtractResponse = T extends S.Codec ? T["Type"] - : T extends unknown ? void - : never - -export type ExtractEResponse = T extends S.Codec ? T["Encoded"] - : T extends unknown ? void - : never - -export interface ClientForOptions { - readonly skipQueryKey?: readonly string[] - /** - * Middleware tag to attach to every rpc on the client. Schema-only — the - * client never invokes the middleware (no Live impl required), but its - * declared `error` schema joins the rpc failure union via - * `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. Required when - * middleware can throw errors that aren't part of the resource's declared - * error union (e.g. auth middleware throwing `NotLoggedInError`); without - * it the client decode would fail with a `SchemaError` for stream rpcs. - */ -} - -// $Project/$Configuration.Index -// -> "$Project", "$Configuration", "Index" -export const makeQueryKey = ({ id, options }: { id: string; options?: ClientForOptions }) => - id - .split("/") - .filter((segment: string) => !options || !options.skipQueryKey?.includes(segment)) - .map((segment: string) => "$" + segment) - .join(".") - .split(".") - -export interface RequestHandlerWithInput { - handler: (i: I) => Effect.Effect - id: Id - options?: ClientForOptions - Request: Request - queryKeyProjectionHash?: string - disableQueryInvalidation?: boolean -} - -/** Type alias: a no-input handler is simply `RequestHandlerWithInput`. */ -export type RequestHandler = RequestHandlerWithInput< - void, - A, - E, - R, - Request, - Id -> - -export interface RequestStreamHandlerWithInput { - handler: (i: I) => Stream.Stream - id: Id - options?: ClientForOptions - Request: Request - /** - * Phantom type property (never set at runtime) that carries the `Final` type to - * `StreamMutationWithExtensions`. The tilde prefix follows the Effect convention for - * phantom/virtual properties and prevents accidental runtime access. - * Stream failures bubble through the execute effect's typed error channel `E`; - * the reactive `AsyncResult` ref also mirrors the failure for live progress UI. - */ - readonly "~final"?: Final -} - -/** Type alias: a no-input stream handler is simply `RequestStreamHandlerWithInput`. */ -export type RequestStreamHandler = - RequestStreamHandlerWithInput - -// make sure this is exported or d.ts of apiClientFactory breaks?! -export type RequestInputFromMake any }> = Parameters extends - [] ? void : Parameters[0] - -// Has no input only when the request schema declares no payload fields (the auto-added -// `_tag` field is ignored). Any payload fields (even all-optional) produce a function handler. -type HasNoFields = I extends { readonly fields: infer F extends S.Struct.Fields } - ? [Exclude] extends [never] ? true : false - : false - -type RequestInput any }> = Parameters[0] - -/** - * Caller-facing input type for a request. `void` when the request schema has no fields; - * otherwise `make`'s first param type. - */ -export type HandlerInput any }> = HasNoFields extends true ? void - : RequestInput - -/** Extracts the final-value type from a stream request. Defaults to the success type when no `final` schema is set. */ -type FinalTypeOf = T extends { readonly final: infer F extends S.Top } ? F["Type"] - : T["success"]["Type"] - -// `T["success"]` / `T["error"]` are constrained to `S.Top` via `Req`, so we -// can read `["DecodingServices"]` directly. Avoids the conditional in -// `S.Codec.DecodingServices = X extends Top ? X["DecodingServices"] : never`, -// which tsgo (native) fails to reduce in this generic position and leaves as -// `unknown`, polluting the `R` channel of every client handler. -type RequestHandlerFor = T["stream"] extends true - ? RequestStreamHandlerWithInput< - HandlerInput, - T["success"]["Type"], - T["error"]["Type"] | E, - R | T["success"]["DecodingServices"] | T["error"]["DecodingServices"], - T, - Id, - FinalTypeOf - > - : RequestHandlerWithInput< - HandlerInput, - T["success"]["Type"], - T["error"]["Type"] | E, - R | T["success"]["DecodingServices"] | T["error"]["DecodingServices"], - T, - Id - > - -export type RequestHandlers = { - [K in keyof M as M[K] extends Req ? K : never]: Extract extends infer T extends Req - ? RequestHandlerFor - : never -} diff --git a/packages/effect-app/src/client/errors.ts b/packages/effect-app/src/client/errors.ts deleted file mode 100644 index 988b45f4d..000000000 --- a/packages/effect-app/src/client/errors.ts +++ /dev/null @@ -1,310 +0,0 @@ -/** @effect-diagnostics overriddenSchemaConstructor:skip-file */ -import { TaggedErrorClass } from "effect-app/Schema" -import * as Cause from "effect/Cause" -import * as S from "../Schema.ts" - -export const tryToJson = (error: unknown) => { - try { - const errorJson = (error as any).toJSON() - return errorJson && typeof errorJson === "object" ? errorJson as Record : { error } - } catch { - try { - return { error: (error as any).toString() } - } catch (err) { - try { - return { error: `Failed to convert error: ${err}` } - } catch { - return { error: `Failed to convert error: unknown failure` } - } - } - } -} - -// eslint-disable-next-line unused-imports/no-unused-vars -// @ts-expect-error type not used -export class NotFoundError extends TaggedErrorClass>()("NotFoundError", { - type: S.String, - id: S.Unknown -}) { - constructor( - props: { type: string; id: unknown; cause?: unknown }, - disableValidation?: boolean - ) { - super(props, disableValidation as any) - } - override get message() { - return `Didn't find ${(this as any).type}#${JSON.stringify((this as any).id)}` - } - override toString() { - return `NotFoundError: ${this.message}` - } -} - -const messageFallback = (messageOrObject?: string | { message: string }) => - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject ?? "" } - -export class InvalidStateError extends TaggedErrorClass()("InvalidStateError", { - message: S.String -}) { - constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super( - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, - disableValidation as any - ) - } - override toString() { - return `InvalidStateError: ${this.message}` - } -} - -export class ServiceUnavailableError extends TaggedErrorClass()("ServiceUnavailableError", { - message: S.String -}) { - constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super( - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, - disableValidation as any - ) - } - override toString() { - return `ServiceUnavailableError: ${this.message}` - } -} - -export class ValidationError extends TaggedErrorClass()("ValidationError", { - errors: S.Array(S.Unknown) -}) { - constructor( - props: { errors: ReadonlyArray; cause?: unknown }, - disableValidation?: boolean - ) { - super(props, disableValidation as any) - } - override get message() { - return `Validation failed: ${(this as any).errors.map((e: any) => JSON.stringify(e, undefined, 2)).join(",\n")}` - } - override toString() { - return `ValidationError: ${this.message}` - } -} - -export class NotLoggedInError extends TaggedErrorClass()("NotLoggedInError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `NotLoggedInError: ${this.message}` - } -} - -/** - * The user carries a valid Userprofile, but there is a problem with the login none the less. - */ -export class LoginError extends TaggedErrorClass()("NotLoggedInError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `LoginError: ${this.message}` - } -} - -export class UnauthorizedError extends TaggedErrorClass()("UnauthorizedError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `UnauthorizedError: ${this.message}` - } -} - -type OptimisticConcurrencyDetails = { - readonly type: string - readonly id: string - readonly code: number - readonly current?: string | undefined - readonly found?: string | undefined -} - -export class OptimisticConcurrencyException extends TaggedErrorClass()( - "OptimisticConcurrencyException", - { message: S.String } -) { - readonly details?: OptimisticConcurrencyDetails - readonly raw?: unknown - constructor( - args: - | OptimisticConcurrencyDetails - | ({ message: string; cause?: unknown; raw?: unknown }), - disableValidation?: boolean - ) { - super( - "message" in args ? args : { message: `Existing ${args.type} ${args.id} record changed` }, - disableValidation as any - ) - if (!("message" in args)) { - this.details = args - } - } - override toString() { - return `OptimisticConcurrencyException: ${this.message}` - } -} - -/** - * Raised by a store adapter when a database operation fails for an - * infrastructure reason (request timeout, throttle, 5xx, dropped socket, or any - * non-conflict error). Distinct from `OptimisticConcurrencyException`, which is - * an expected etag conflict. - * - * `transient` is set by the adapter for failures worth retrying (timeout / - * throttle / 5xx / network). `cause` is the underlying adapter error, carried - * through a `Defect` schema so it serializes (an `Error` encodes to - * `{ name, message, cause }`) instead of breaking JSON encoding of the channel. - * - * Treated by the api/client/FE machinery like an unexpected (500-class) error. - */ -export class DatabaseError extends TaggedErrorClass()( - "DatabaseError", - { - message: S.String, - transient: S.Boolean, - cause: S.optional(S.Defect()) - } -) { - constructor( - args: { message?: string; transient?: boolean; cause?: unknown }, - disableValidation?: boolean - ) { - super( - { - message: args.message ?? "Database operation failed", - transient: args.transient ?? false, - cause: args.cause - }, - disableValidation as any - ) - } - override toString() { - return `DatabaseError: ${this.message}` - } -} - -const MutationOnlyErrors = [ - InvalidStateError, - OptimisticConcurrencyException -] as const - -const GeneralErrors = [ - NotFoundError, - NotLoggedInError, - LoginError, - UnauthorizedError, - ValidationError, - ServiceUnavailableError, - DatabaseError -] as const - -export const SupportedErrors = S.Union([ - ...MutationOnlyErrors, - ...GeneralErrors -]) -// .pipe(named("SupportedErrors")) -// .pipe(withDefaultMake) -export type SupportedErrors = S.Schema.Type - -// ideal? -// export const QueryErrors = union({ ...GeneralErrors }) -// .pipe(named("QueryErrors")) -// .pipe(withDefaultMake) -// export type QueryErrors = Schema.Type -// export const MutationErrors = union({ ...GeneralErrors, ...GeneralErrors }) -// .pipe(named("MutationErrors")) -// .pipe(withDefaultMake) - -// export type MutationErrors = Schema.Type - -export const MutationErrors = SupportedErrors -export const QueryErrors = SupportedErrors -export type MutationErrors = S.Schema.Type -export type QueryErrors = S.Schema.Type - -export const ErrorSilenced = Symbol.for("effect-app/error-silenced") -export const isErrorSilenced = (e: unknown): boolean => - typeof e === "object" && e !== null && ErrorSilenced in e ? !!e[ErrorSilenced] : false -export const silenceError = (e: Record) => { - e[ErrorSilenced] = true -} - -export class CauseException extends Error { - readonly originalCause: Cause.Cause - readonly _tag: string - - constructor(originalCause: Cause.Cause, _tag: string) { - const limit = Error.stackTraceLimit - Error.stackTraceLimit = 0 - super() - Error.stackTraceLimit = limit - this.originalCause = originalCause - this._tag = _tag - this.cause = Cause.squash(originalCause) - // v4: makeFiberFailure removed — use Cause.prettyErrors instead - const errors = Cause.prettyErrors(originalCause) - const first = errors[0] - if (first) { - this.name = first.name - this.message = first.message - if (first.stack) { - this.stack = first.stack - } - } else { - this.name = "CauseException" - this.message = Cause.pretty(originalCause) - } - } - toReport() { - return { - _tag: this._tag, - name: this.name, - message: this.message - } - } - - toJSON() { - return { - _tag: this._tag, - name: this.name, - message: this.message, - originalCause: this.originalCause - } - } - - [Symbol.for("nodejs.util.inspect.custom")]() { - return this.toJSON() - } - override toString() { - return `[${this._tag}] ` + Cause.pretty(this.originalCause) - } -} - -export const tryToReport = (error: { toReport(): Record; toString(): string }) => { - try { - return error.toReport() - } catch { - try { - return { error: error.toString() } - } catch (err) { - try { - return { error: `Failed to convert error: ${err}` } - } catch { - return { error: `Failed to convert error: unknown failure` } - } - } - } -} diff --git a/packages/effect-app/src/client/makeClient.ts b/packages/effect-app/src/client/makeClient.ts deleted file mode 100644 index c1785f084..000000000 --- a/packages/effect-app/src/client/makeClient.ts +++ /dev/null @@ -1,629 +0,0 @@ -import type * as Exit from "effect/Exit" -import { type GetContextConfig, type RpcContextMap } from "../rpc/RpcContextMap.ts" -import * as S from "../Schema.ts" -import { AST } from "../Schema.ts" -import type { ClientForOptions } from "./clientFor.ts" - -/** - * Minimal structural shape for an rpc-client middleware tag. - * Captures only what `makeRpcClient` and routing/client factory consume from it - * (no `Default` layer required — that is provided to `makeRouter`). - */ -export interface ClientMiddleware> { - readonly requestContextMap: RequestContextMap - readonly requestContext: unknown - readonly provides?: unknown - readonly requires?: unknown -} - -const merge = (a: any, b: Array) => - a !== undefined && b.length ? S.Union([a, ...b]) : a !== undefined ? a : b.length ? S.Union(b) : S.Never - -type SchemaOrFields = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct : S.Void - -type TaggedRequestSchema = S.Struct< - { readonly _tag: S.tag } & Payload -> - -type QueryOnlyRequests = { - [K in keyof Resource as Resource[K] extends { readonly type: "query" } ? K : never]: Resource[K] -} - -type QueryOnlyResources = { - [K in keyof Resources]: QueryOnlyRequests -} - -type InputFromPayload = keyof Payload extends never ? void - : S.Struct["Type"] - -type OutputFromSuccess = Success extends typeof S.Void ? void : Success["Type"] - -type InvalidationResources = Record> - -/** - * An invalidation instruction returned from `invalidatesQueries`. One of: - * - a raw query key (`ReadonlyArray`) - * - an RPC handler object (`{ id, options? }`) — query key derived from `id` - * - a compatibility `{ filters: { queryKey }, options? }` shape - * - * Predicate filters are intentionally not represented: atom invalidation is keyed by - * concrete query keys. - */ -export type InvalidateQueryInstruction< - Filters = { readonly queryKey: ReadonlyArray }, - Options = Record -> = - | ReadonlyArray - | { - readonly id: string - readonly options?: ClientForOptions - } - | { - readonly filters: Filters - readonly options?: Options - } - -export type InvalidationCallback = ( - queryKey: readonly string[], - resources: QueryOnlyResources, - ...args: [Input] extends [void] ? [exit: Exit.Exit] - : [input: Input, exit: Exit.Exit] -) => ReadonlyArray - -export type InvalidationConfig = { - readonly invalidatesQueries: InvalidationCallback - readonly invalidationResources?: Resources - readonly disableQueryInvalidation?: boolean -} - -type InvalidationConfigForCommand< - Resources, - Payload extends S.Struct.Fields, - Success extends S.Top, - Error extends S.Top -> = InvalidationConfig< - Resources, - InputFromPayload, - OutputFromSuccess, - Error["Type"] -> - -export const configureInvalidation = () => -( - invalidatesQueries: InvalidationCallback, NoInfer, NoInfer> -): InvalidationConfig => ({ invalidatesQueries }) - -export const configureInvalidationCallback = () => -( - invalidatesQueries: InvalidationCallback, NoInfer, NoInfer> -): InvalidationCallback => invalidatesQueries - -export const configureInvalidationResources = () => - ({}) as Pick, "invalidationResources"> - -type TaggedRequestForResult< - Self, - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top, - Error extends S.Top, - Config, - ModuleName extends string, - Type extends "command" | "query", - Stream extends boolean, - Resources = never, - Final extends S.Top = never, - Middleware = unknown -> = - & S.Opaque, {}> - & { - readonly fields: TaggedRequestSchema["fields"] - readonly _tag: Tag - readonly success: Success - readonly error: Error - readonly config: Config - readonly id: `${ModuleName}.${Tag}` - readonly moduleName: ModuleName - readonly type: Type - readonly stream: Stream - readonly middleware?: Middleware - readonly "~invalidationResources"?: Resources - } - & ([Final] extends [never] ? {} : { readonly final: Final }) - -export const makeRpcClient = < - Middleware extends ClientMiddleware>, - GeneralErrors extends S.Top = never ->(middleware: Middleware, generalErrors?: GeneralErrors) => { - // Long way around Context/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??) - type ServiceMap = { - success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields - error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields - final?: S.Top | S.Struct.Fields // optional final-value schema for stream requests - stream?: boolean // request metadata — stripped from stored config - } - - type RequestConfig = GetContextConfig - - // Errors raised by RPC middleware (e.g. `NotLoggedInError` from auth) used to - // be merged into `resource.error` here so they would surface in the wire - // failure schema. That merging is gone — the canonical source for middleware - // errors is now the middleware tag attached to the rpc on both server and - // client (see `ApiClientFactory.makeFor({ middleware })`). `Rpc.exitSchema` - // unions in `rpc.middlewares[*].error` automatically. - type MergeError = [GeneralErrors] extends [never] ? SchemaOrFields : S.Union<[SchemaOrFields, GeneralErrors]> - type ErrorResult = C extends { error: infer E } ? MergeError - : [GeneralErrors] extends [never] ? typeof S.Never - : GeneralErrors - - function makeRequestClass>( - tag: Tag, - fields: Fields, - config?: C - ) { - const failureSchema = merge( - config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined, - [generalErrors].filter(Boolean) - ) - const successSchema = config?.success - ? S.isSchema(config.success) - ? AST.isVoid(config.success.ast) ? S.Void : config.success - : S.Struct(config.success) - : S.Void - - const finalConfig = (config as any)?.final - const finalSchema = finalConfig && S.isSchema(finalConfig) ? finalConfig : undefined - - // Strip stream from the stored config — it's request metadata, not handler config - const { stream: _stream, ...restConfig } = config ?? ({} as C) - - const RequestClass = S.Opaque()(S.TaggedStruct(tag, fields)) - Object.assign(RequestClass, { - _tag: tag, - success: successSchema, - error: failureSchema, - ...(finalSchema !== undefined ? { final: finalSchema } : {}), - config: restConfig - }) - - return RequestClass - } - - function makeTaggedRequestWithMeta< - ModuleName extends string, - Type extends "command" | "query" - >( - moduleName: ModuleName, - type: Type - ) { - function TaggedRequestWithMeta(): { - // ─── stream: true overloads (must come before non-stream so TypeScript picks them) ─────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record = RequestConfig & Record - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; success: Success; error: Error; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record & { error?: never } = - & RequestConfig - & Record - & { error?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; success: Success; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { success: Success } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - // ─── stream: true without `success` overloads ──────────────────────────────────────────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - C extends RequestConfig & Record & { success?: never } = - & RequestConfig - & Record - & { success?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; error: Error }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & { error: Error } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - typeof S.Void, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - never, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - C extends RequestConfig & Record & { success?: never; error?: never } = - & RequestConfig - & Record - & { success?: never; error?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & Partial>>, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - never, - Middleware - > - // ─── non-stream overloads ──────────────────────────────────────────────────────────────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record = RequestConfig & Record - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { success: Success; error: Error; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record & { error?: never } = - & RequestConfig - & Record - & { - error?: never - } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { success: Success; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - C extends RequestConfig & Record & { success?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { error: Error }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & { - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - typeof S.Void, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - never, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - C extends RequestConfig & Record & { success?: never; error?: never } - >( - tag: Tag, - fields: Payload, - config: Omit, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & Partial>>, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - never, - Middleware - > - ( - tag: Tag, - fields: Payload - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult<{}>, - // Empty config, NOT `Record`: the config type flows into - // `GetEffectContext`/`GetEffectError` as `T`, and `keyof Record` - // is `string` — so an inverted key like `allowAnonymous` would satisfy - // `key extends keyof T`, then `T[key]` (`never`) `extends true` (true, since - // `never` is assignable to everything) and the middleware wrongly takes the - // "allowAnonymous: true" (anonymous) branch, dropping `UserProfile`. `{}` has - // `keyof {} = never`, so no config key is treated as set and the defaults - // (auth required, `UserProfile` provided) apply — same as passing `{}` explicitly. - {}, - ModuleName, - Type, - false, - never, - never, - Middleware - > - } { - return (( - tag: Tag, - fields: Fields, - config?: C, - invalidatesQueries?: InvalidationCallback - ) => { - const isStream = (config as any)?.stream === true - const requestConfig = invalidatesQueries === undefined ? config : { ...config, invalidatesQueries } - const cls = makeRequestClass(tag, fields, requestConfig) - Object.assign(cls, { - id: `${moduleName}.${tag}`, - moduleName, - type, - stream: isStream, - middleware - }) - return cls - }) as any - } - return Object.assign(TaggedRequestWithMeta, { moduleName, type } as const) - } - - function TaggedRequestFor(moduleName: ModuleName) { - const Query = makeTaggedRequestWithMeta(moduleName, "query") - const Command = makeTaggedRequestWithMeta(moduleName, "command") - - return { - moduleName, - /** - * Create query request classes for this module. - * Queries read state and should not mutate server state. - * Pass `stream: true` in the config to produce a Stream of `success` values (QueryStream behaviour). - */ - Query, - /** - * Create command request classes for this module. - * Commands mutate state and should avoid returning complex read models. - * Pass `stream: true` in the config to produce a Stream of `success` values (CommandStream behaviour). - */ - Command - } as const - } - - return { - TaggedRequestFor - } -} diff --git a/packages/effect-app/src/faker.ts b/packages/effect-app/src/faker.ts deleted file mode 100644 index 601683305..000000000 --- a/packages/effect-app/src/faker.ts +++ /dev/null @@ -1,32 +0,0 @@ -// FILE HAS SIDE EFFECTS! -import type { Faker } from "@faker-js/faker" -import * as FC from "fast-check" - -// TODO: inject faker differently, so we dont care about multiple instances of library. - -// eslint-disable-next-line prefer-const -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let faker: Faker = undefined as any as Faker -export function setFaker(f: Faker) { - faker = f -} - -export function getFaker() { - if (!faker) throw new Error("You forgot to load faker library") - return faker -} - -export const fakerToArb = (fakerGen: () => T) => (fc: typeof FC) => { - return FC.noBias(FC.noShrink( - fc - .integer() - .map((seed) => { - faker.seed(seed) // seed the generator - return fakerGen() // call it - }) - )) -} - -export const fakerArb = ( - gen: (fake: Faker) => () => T -): (a: any) => FC.Arbitrary => fakerToArb(() => gen(getFaker())()) diff --git a/packages/effect-app/src/http.ts b/packages/effect-app/src/http.ts deleted file mode 100644 index 51bad709f..000000000 --- a/packages/effect-app/src/http.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./http/internal/lib.ts" diff --git a/packages/effect-app/src/http/Request.ts b/packages/effect-app/src/http/Request.ts deleted file mode 100644 index c42206ee1..000000000 --- a/packages/effect-app/src/http/Request.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { HttpClientResponse } from "effect/http/HttpClientResponse" -import * as Option from "effect/Option" -import * as Effect from "../Effect.ts" -import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.ts" - -export interface ResponseWithBody extends Pick { - readonly body: A -} - -// TODO: consider rebuilding the text/json helpers to use a cached effect -// https://discord.com/channels/795981131316985866/1098177242598756412/1168565257569046712 - -export const responseWithJsonBody = ( - response: HttpClientResponse -) => - Effect.map(response.json, (body): ResponseWithBody => ({ - body, - headers: response.headers, - status: response.status, - remoteAddress: response.remoteAddress - })) - -export const demandJson = (client: HttpClient.HttpClient) => - HttpClient - .mapRequest(client, (_) => HttpClientRequest.acceptJson(_)) - .pipe(HttpClient.transform((r, request) => - Effect.tap(r, (response) => - Option - .exists( - HttpHeaders.get(response.headers, "Content-Type"), - (_) => _.startsWith("application/json") - ) - ? Effect.void - : Effect.fail( - new HttpClientError.DecodeError({ - request, - response, - description: "not json response: " - + Option.getOrElse(HttpHeaders.get(response.headers, "Content-Type"), () => "") - }) - )) - )) diff --git a/packages/effect-app/src/http/internal/lib.ts b/packages/effect-app/src/http/internal/lib.ts deleted file mode 100644 index 6c035c281..000000000 --- a/packages/effect-app/src/http/internal/lib.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * as HttpHeaders from "effect/http/Headers" -export * as HttpBody from "effect/http/HttpBody" -export * as HttpClient from "effect/http/HttpClient" -export * as HttpClientError from "effect/http/HttpClientError" -export * as HttpClientRequest from "effect/http/HttpClientRequest" -export * as HttpClientResponse from "effect/http/HttpClientResponse" -// HttpLayerRouter removed in v4 — use HttpRouter instead -export * as HttpMiddleware from "effect/http/HttpMiddleware" -export * as HttpRouter from "effect/http/HttpRouter" -export * as HttpServer from "effect/http/HttpServer" -export * as HttpServerError from "effect/http/HttpServerError" -export * as HttpServerRequest from "effect/http/HttpServerRequest" -export * as HttpServerResponse from "effect/http/HttpServerResponse" diff --git a/packages/effect-app/src/ids.ts b/packages/effect-app/src/ids.ts deleted file mode 100644 index 09dc25f4b..000000000 --- a/packages/effect-app/src/ids.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema" -import type { B } from "effect-app/Schema/schema" -import * as Effect from "effect/Effect" -import type { Simplify } from "effect/Types" -import * as S from "./Schema.ts" -import { extendM } from "./utils.ts" - -export interface RequestIdBrand extends StringIdBrand { - readonly RequestId: unique symbol -} - -export type RequestId = NonEmptyString255 -/** - * Schema for a request id. - * - * A request id may be made from a span id, which does not comply with the - * `StringId` schema (hence the looser `NonEmptyString255` base). - * - * `.withConstructorDefault` => fresh `StringId` (construction-only; not - * applied during decode — cannot be used to JIT-migrate database fields). - * See `./Schema/ext.ts` for the full policy note. - */ -const requestIdSamples = S.makeFilter(() => undefined, { - arbitraryConstraint: { - minLength: 21, - maxLength: 21, - patterns: [{ source: "^[A-Za-z0-9_-]{21}$", flags: "" }] - } -}) - -export const RequestId = extendM( - Object - .assign( - Object.create(NonEmptyString255) as {}, - NonEmptyString255.pipe(S.check(requestIdSamples)) as unknown as Codec - ), - (s) => { - function make(): NonEmptyString255 - function make(input: string, options?: S.MakeOptions): NonEmptyString255 - function make(input?: string, options?: S.MakeOptions): NonEmptyString255 { - const id = input === undefined ? StringId.make() : StringId.make(input, options) - return id - } - return ({ - make, - /** - * Construction-only default: fresh `StringId`. Applied only when the - * field is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See `./Schema/ext.ts` - * file-level note. - */ - withConstructorDefault: S.withConstructorDefault(Effect.sync(make))(s as typeof s & S.WithoutConstructorDefault) - }) - } -) - .pipe(withDefaultMake) - -export interface UserProfileIdBrand extends Simplify & StringIdBrand> {} -export type UserProfileId = string & UserProfileIdBrand -/** - * Branded `StringId` for user profiles. - * - * Exposes `.withConstructorDefault` (fresh id) — construction-only; not - * applied during decode. See `./Schema/ext.ts` for the full policy note. - */ -export const UserProfileId = brandedStringId() diff --git a/packages/effect-app/src/index.ts b/packages/effect-app/src/index.ts deleted file mode 100644 index 5f15b05be..000000000 --- a/packages/effect-app/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -// eslint-disable-next-line import/no-unassigned-import -import "./builtin.ts" - -export * as Fnc from "./Function.ts" -export * as Utils from "./utils.ts" - -export * as Array from "./Array.ts" -export * as Config from "./Config.ts" -export * as ConfigProvider from "./ConfigProvider.ts" -export * as Context from "./Context.ts" -export * as Effect from "./Effect.ts" -export * as Layer from "./Layer.ts" -export * as Model from "./Model.ts" -export * as NonEmptySet from "./NonEmptySet.ts" -export * as Set from "./Set.ts" -export * as Store from "./Store.ts" - -export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.ts" - -export * from "effect" - -export type * as Types from "./Types.ts" - -export * as SecretURL from "./Config/SecretURL.ts" -export * as RpcX from "./rpc.ts" -export * as S from "./Schema.ts" -export { copy } from "./utils.ts" diff --git a/packages/effect-app/src/logger.ts b/packages/effect-app/src/logger.ts deleted file mode 100644 index 3b1c9822b..000000000 --- a/packages/effect-app/src/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { makeLog } from "effect-app/utils/logger" - -export const PreludeLogger = makeLog("effect-app", "info") diff --git a/packages/effect-app/src/middleware.ts b/packages/effect-app/src/middleware.ts deleted file mode 100644 index 4cb4937af..000000000 --- a/packages/effect-app/src/middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Context from "effect-app/Context" -import { RpcMiddleware } from "./rpc.ts" - -export class DevMode extends Context.Reference("DevMode", { defaultValue: () => false }) {} - -export class RequestCacheMiddleware extends RpcMiddleware.Tag()("RequestCacheMiddleware") {} - -export class ConfigureInterruptibilityMiddleware - extends RpcMiddleware.Tag()("ConfigureInterruptibilityMiddleware") -{} - -export class LoggerMiddleware extends RpcMiddleware.Tag()("LoggerMiddleware") {} - -export class DevModeMiddleware extends RpcMiddleware.Tag()("DevModeMiddleware") {} - -/** - * Generic middlewares attached by `makeRouter` to every request. - * - * Invalidation key wrap/unwrap is handled by the routing layer (server) and the - * api client factory (client) directly — there is no middleware tag for it. - */ -export const DefaultGenericMiddlewares = [ - RequestCacheMiddleware, - ConfigureInterruptibilityMiddleware, - LoggerMiddleware, - DevModeMiddleware -] as const diff --git a/packages/effect-app/src/rpc.ts b/packages/effect-app/src/rpc.ts deleted file mode 100644 index e07797b67..000000000 --- a/packages/effect-app/src/rpc.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * as Invalidation from "./rpc/Invalidation.ts" -export * as MiddlewareMaker from "./rpc/MiddlewareMaker.ts" -export * as RpcContextMap from "./rpc/RpcContextMap.ts" -export * as RpcMiddleware from "./rpc/RpcMiddleware.ts" diff --git a/packages/effect-app/src/rpc/Invalidation.ts b/packages/effect-app/src/rpc/Invalidation.ts deleted file mode 100644 index 2e6db6dee..000000000 --- a/packages/effect-app/src/rpc/Invalidation.ts +++ /dev/null @@ -1,278 +0,0 @@ -import * as Ref from "effect/Ref" -import { Rpc } from "effect/rpc" -import { type ClientForOptions, makeQueryKey } from "../client/clientFor.ts" -import * as Context from "../Context.ts" -import * as DataDependencies from "../DataDependencies.ts" -import * as Effect from "../Effect.ts" -import * as S from "../Schema.ts" - -/** - * Shorthand for a handler-derived invalidation key. - * Accepts an RPC handler object so `add(GetMe)` is equivalent to - * `add(makeQueryKey(GetMe))`. - */ -export type InvalidationKeyInput = InvalidationKey | { readonly id: string; readonly options?: ClientForOptions } - -const normalizeKey = (input: InvalidationKeyInput): InvalidationKey => { - if (Array.isArray(input)) return input - const handler = input as { id: string; options?: ClientForOptions } - return makeQueryKey(handler.options ? { id: handler.id, options: handler.options } : { id: handler.id }) -} - -// Disambiguates `add` overloads: a `string[]` means a single key; an array -// containing arrays-or-objects means a batch of inputs. -const isBatch = ( - input: InvalidationKeyInput | ReadonlyArray -): input is ReadonlyArray => - Array.isArray(input) && input[0] !== undefined && typeof input[0] !== "string" - -const normalizeInputs = ( - input: InvalidationKeyInput | ReadonlyArray -): ReadonlyArray => isBatch(input) ? input.map(normalizeKey) : [normalizeKey(input)] - -/** - * A single segment within an `InvalidationKey` array. - * Accepts any JSON-compatible value: string, number, boolean, null, - * arrays and objects recursively — matching TanStack Query's `queryKey` element type. - */ -export const InvalidationKeySegment = S.Json -export type InvalidationKeySegment = S.Schema.Type - -/** Schema for a single invalidation key – an array of segments compatible with TanStack Query `queryKey`. */ -export const InvalidationKey = S.Array(InvalidationKeySegment) -export type InvalidationKey = S.Schema.Type - -/** Schema for the full set of invalidation keys – an array of `InvalidationKey`. */ -export const InvalidationKeys = S.Array(InvalidationKey) -export type InvalidationKeys = S.Schema.Type - -/** Metadata included in every command response for server-driven cache invalidation. */ -export const CommandMetaData = S.Struct({ - invalidateQueries: InvalidationKeys, - dataDependencies: DataDependencies.DataDependencySet -}) -export type CommandMetaData = S.Schema.Type - -export const emptyDataDependencies: DataDependencies.DataDependencySet = { - reads: DataDependencies.empty(), - writes: DataDependencies.empty() -} - -export const makeMetaData = ( - invalidateQueries: InvalidationKeys, - dataDependencies: DataDependencies.DataDependencySet = emptyDataDependencies -): CommandMetaData => ({ invalidateQueries, dataDependencies }) - -export const QueryResponseWithMetaData = (success: S) => - S.Struct({ payload: success, metadata: S.Struct({ dataDependencies: DataDependencies.DataDependencySet }) }) - -/** - * Wraps a command's success schema so that the wire format carries both the `payload` - * (the handler's actual return value) and `metadata` (server-driven cache invalidation keys). - * Transparent to users: the server handler returns the plain payload and the client receives - * the plain payload — wrapping/unwrapping is handled internally by the routing layer. - */ -export const CommandResponseWithMetaData = (success: S) => - S.Struct({ payload: success, metadata: CommandMetaData }) - -/** - * Wraps a command's failure schema so that the wire format carries both the `error` - * (the handler's actual failure value) and `metadata` (server-driven cache invalidation keys - * accumulated thus far before the failure occurred). - * Transparent to users: the server handler fails with the plain error and the client receives - * the plain error — wrapping/unwrapping is handled internally by the routing layer. - */ -export const CommandFailureWithMetaData = (error: E) => - S.Struct({ _tag: S.Literal("CommandFailureWithMetaData"), error, metadata: CommandMetaData }) - -/** - * Stream chunk schema for stream responses with metadata. - * Each item is either a data value, an intermediate "metadata" signal carrying cache - * invalidation keys accumulated since the previous drain, or a final "done" signal. - * Transparent to users: stream handlers return plain values and clients receive plain values — - * wrapping/unwrapping is handled internally by the routing layer. - * - * The "done" chunk is always the last item in the stream and carries any remaining invalidation - * keys. An optional "metadata" chunk may appear after any "value" chunk and carries keys - * accumulated since the last drain (V3: mid-stream invalidation). - */ -export const StreamResponseChunk = (success: S) => - S.Union([ - S.Struct({ _tag: S.Literal("value"), value: success }), - S.Struct({ _tag: S.Literal("metadata"), metadata: CommandMetaData }), - S.Struct({ _tag: S.Literal("done"), metadata: CommandMetaData }) - ]) - -export type StreamResponseChunk = - | { readonly _tag: "value"; readonly value: A } - | { readonly _tag: "metadata"; readonly metadata: CommandMetaData } - | { readonly _tag: "done"; readonly metadata: CommandMetaData } - -/** - * Stream chunk schema for stream failures with metadata. - * Used to signal a stream failure while still carrying cache invalidation keys - * accumulated thus far. - */ -export const StreamFailureChunk = (error: E) => - S.Struct({ _tag: S.Literal("error"), error, metadata: CommandMetaData }) - -export type StreamFailureChunk = { readonly _tag: "error"; readonly error: E; readonly metadata: CommandMetaData } - -/** - * Context annotation for declaring static cache invalidation keys on a low-level `Rpc` definition. - * These keys are always included in the command response metadata, regardless of the handler logic. - * - * Prefer using `makeQueryKey` over raw string arrays to stay in sync with the actual query - * definitions without manual string maintenance: - * - * ```ts - * import { makeQueryKey } from "effect-app/client" - * import { Invalidation } from "effect-app/rpc" - * import * as UserRsc from "../User/index.ts" // separate module to avoid circular deps - * - * class UpdateProfile extends Rpc.make("UpdateProfile", { ... }) - * .annotate(Invalidation.Invalidates, [makeQueryKey(UserRsc.GetMe), makeQueryKey(UserRsc.GetProfile)]) {} - * ``` - * - * **Circular dependency note:** if mutations and queries live in the same file you may hit a - * circular reference at evaluation time. The idiomatic fix is to move mutations into their own - * module (e.g. `User/mutations.ts`) that directly imports the relevant query classes rather than - * re-exporting them through a barrel. - * - * For the higher-level `Command`/`Query` builders from `makeRpcClient`, use the - * `invalidatesQueries` callback argument instead (it receives the same query keys at runtime). - */ -export const Invalidates = Context.Reference>( - "effect-app/rpc/Invalidates", - { defaultValue: () => [] } -) -export type Invalidates = typeof Invalidates - -/** The shape of the per-request service that accumulates invalidation keys. */ -export interface InvalidationSetService { - readonly add: ( - input: InvalidationKeyInput | ReadonlyArray - ) => Effect.Effect - readonly get: Effect.Effect> - /** - * V3: Reads all currently accumulated keys and resets the bucket to empty. - * Used by the stream routing layer to emit intermediate "metadata" chunks - * without re-sending keys that have already been forwarded to the client. - */ - readonly drain: Effect.Effect> -} - -/** - * Request-scoped service for accumulating invalidation keys dynamically inside a handler. - * Provided by `InvalidationMiddlewareLive` for every RPC call; has a no-op default so it is - * safe to use even when the HTTP middleware is absent (tests, workers, etc.). - * - * Use `InvalidationSet.add(key)` as a shorthand to skip `.use(_ => _.add(key))`. The - * underlying service is still available via `.use` / `.useSync` for advanced cases. - * - * `add` accepts an RPC handler directly (e.g. `UserRsc.GetMe`) — its query key is derived via - * `makeQueryKey` so keys stay in sync with the actual query definitions. Raw `InvalidationKey` - * arrays and arrays of either form are also accepted. - * - * ```ts - * import * as Effect from "effect/Effect" - * import { Invalidation } from "effect-app/rpc" - * import * as CartRsc from "../Cart/queries.ts" - * import * as UserRsc from "../User/queries.ts" - * - * const handler = Effect.fnUntraced(function*(req: UpdateCartRequest) { - * const cart = yield* CartRepo.save(req.cart) - * - * // single handler - * yield* Invalidation.InvalidationSet.add(UserRsc.GetMe) - * - * // batch - * if (cart.isCheckedOut) { - * yield* Invalidation.InvalidationSet.add([CartRsc.GetCartStats, UserRsc.GetMe]) - * } - * - * return cart - * }) - * ``` - * - * You can combine static (`Invalidates` annotation) and dynamic (`InvalidationSet.use`) keys: - * the annotation pre-populates the set before the handler runs; dynamic additions accumulate - * throughout the handler. All keys are included in the command response metadata. - */ -const InvalidationSetRef = Context.Reference( - "effect-app/rpc/InvalidationSet", - { - defaultValue: () => ({ - add: (_input: InvalidationKeyInput | ReadonlyArray) => Effect.void, - get: Effect.succeed([] as ReadonlyArray), - drain: Effect.succeed([] as ReadonlyArray) - }) - } -) -export const InvalidationSet = Object.assign(InvalidationSetRef, { - /** - * Shortcut for `InvalidationSet.use(_ => _.add(input))`. Accepts a single - * `InvalidationKeyInput` or an array of them. - */ - add: (input: InvalidationKeyInput | ReadonlyArray) => - InvalidationSetRef.use((_) => _.add(input)) -}) -export type InvalidationSet = typeof InvalidationSetRef - -/** Creates a fresh `InvalidationSet` implementation backed by a `Ref`. */ -export const makeInvalidationSet = (ref: Ref.Ref>): InvalidationSetService => ({ - add: (input) => Ref.update(ref, (keys) => [...keys, ...normalizeInputs(input)]), - get: Ref.get(ref), - drain: Ref.getAndSet(ref, []) -}) - -/** - * `Rpc.Custom` definition for command RPCs that wrap the success/error schemas - * with `CommandResponseWithMetaData` / `CommandFailureWithMetaData`. The wrap - * lets server forward accumulated invalidation keys on both success and - * handler-thrown failure paths. Middleware-thrown errors bypass the wrap - * (the handler never ran, so no metadata) and flow raw at the Cause level — - * the client decodes them via the `rpc.middlewares[*].error` failure-union - * channel of `Rpc.exitSchema`. - */ -// eslint-disable-next-line import/namespace -export interface CommandRpc extends Rpc.Custom { - readonly out: Rpc.Custom.Out< - ReturnType>, - ReturnType> - > -} - -/** - * Custom Rpc constructor for command RPCs. - * Wraps the success schema with `CommandResponseWithMetaData` and the error - * schema with `CommandFailureWithMetaData`. - */ -export const makeCommandRpc = Rpc.custom(({ defect, error, success }) => ({ - success: CommandResponseWithMetaData(success), - error: CommandFailureWithMetaData(error), - defect -})) - -/** - * `Rpc.Custom` definition for stream RPCs that wrap the success/error schemas - * with `StreamResponseChunk` / `StreamFailureChunk`. - */ -// eslint-disable-next-line import/namespace -export interface StreamRpc extends Rpc.Custom { - readonly out: Rpc.Custom.Out< - ReturnType>, - ReturnType> - > -} - -/** - * Custom Rpc constructor for stream RPCs. - * Wraps the success schema with `StreamResponseChunk` and - * the error schema with `StreamFailureChunk`. - */ -export const makeStreamRpc = Rpc.custom(({ defect, error, success }) => ({ - success: StreamResponseChunk(success), - error: StreamFailureChunk(error), - defect -})) diff --git a/packages/effect-app/src/rpc/MiddlewareMaker.ts b/packages/effect-app/src/rpc/MiddlewareMaker.ts deleted file mode 100644 index 4c5b67469..000000000 --- a/packages/effect-app/src/rpc/MiddlewareMaker.ts +++ /dev/null @@ -1,492 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import { Rpc, type RpcGroup, type RpcSchema } from "effect/rpc" -import { type HandlersFrom } from "effect/rpc/RpcGroup" -import * as S from "effect/Schema" -import type * as Scope from "effect/Scope" -import { type Simplify } from "effect/Types" -import * as Context from "../Context.ts" -import { PreludeLogger } from "../logger.ts" -import { type TypeTestId } from "../TypeTest.ts" -import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.ts" -import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.ts" -import * as RpcMiddlewareX from "./RpcMiddleware.ts" - -// adapter for effect/rpc v3 middleware provides. (in effect-smol (v4), it's just a Service Identifier, no tags.) -// hm? -type MakeTags = A - -export interface MiddlewareMaker< - Self, - Id extends string, - RequestContextMap extends Record, - MiddlewareProviders extends ReadonlyArray -> extends - RpcMiddlewareX.TagClass< - Self, - Id, - Simplify< - & (Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? {} : { - readonly requires: MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - }) - & (MiddlewareMaker.ManyErrors extends never ? {} - : { - readonly error: S.Codec> - }) - & (MiddlewareMaker.ManyProvided extends never ? {} - : { readonly provides: MakeTags> }) - >, - { - provides: MiddlewareMaker.ManyProvided extends never ? never - : MakeTags> - requires: Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never - : MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - } - > -{ - readonly layer: Layer.Layer> - readonly requestContext: RequestContextTag - readonly requestContextMap: RequestContextMap -} - -export interface RequestContextTag> - extends Context.Service<"RequestContextConfig", GetContextConfig> -{} - -export namespace MiddlewareMaker { - export type Any = TagClassAny - - export type ApplyServices = Exclude> | Required - - export type ApplyManyServices, R> = - | Exclude }[number]> - | { [K in keyof A]: Required }[number] - - export type ManyProvided> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Provided }[number] - : Provided - export type ManyRequired> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Required }[number] - : Required - export type ManyErrors> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Errors }[number] - : Errors - - export type Provided = T extends TagClassAny ? T extends { provides: infer _P } ? _P - : never - : never - - export type Errors = T extends TagClassAny ? T extends { error: S.Top } ? T["error"]["Type"] - : never - : never - - export type Required = T extends TagClassAny ? T extends { requires: infer _R } ? _R - : never - : never -} - -// the following implements sort of builder pattern -// we support both sideways and upwards elimination of dependencies - -// it's for dynamic middlewares -type GetDependsOnKeys = MW extends { dependsOn: NonEmptyReadonlyArray } ? { - [K in keyof MW["dependsOn"]]: MW["dependsOn"][K] extends AnyDynamic ? MW["dependsOn"][K]["dynamic"]["key"] - : never - }[keyof MW["dependsOn"]] - : never - -type FilterInDynamicMiddlewares< - MWs extends ReadonlyArray, - RequestContextMap extends Record -> = { - [K in keyof MWs]: MWs[K] extends { dynamic: RpcDynamic } ? MWs[K] - : never -} - -type RecursiveHandleMWsSideways< - MWs, - R extends { - self: any - id: string - rcm: Record - provided: keyof R["rcm"] // that's fine - middlewares: ReadonlyArray - dmp: any - middlewareR: any - } -> = MWs extends [] ? R - : MWs extends [infer F, ...infer Rest extends ReadonlyArray] - ? F extends MiddlewareMaker.Any ? RecursiveHandleMWsSideways[number]["dynamic"]["key"], - // F is fine here because only dynamic middlewares will have 'dependsOn' prop - GetDependsOnKeys - > - middlewares: [...R["middlewares"], F] - dmp: [FilterInDynamicMiddlewares<[F], R["rcm"]>[number]] extends [never] ? R["dmp"] - : - & R["dmp"] - & { - [U in FilterInDynamicMiddlewares<[F], R["rcm"]>[number] as U["dynamic"]["key"]]: U - } - middlewareR: MiddlewareMaker.ApplyManyServices<[F], R["middlewareR"]> - }> - // TypeScript inference fails when checking F extends MiddlewareMaker.Any during F's inference - // if F is a class with static properties - deferring the check avoids this limitation - : `Absurd: F must extend MiddlewareMaker.Any` - : never - -export interface BuildingMiddleware< - Self, - Id extends string, - RequestContextMap extends Record, - Provided extends keyof RequestContextMap, - Middlewares extends ReadonlyArray, - DynamicMiddlewareProviders, - out MiddlewareR extends { _tag: string } = never -> { - rpc: < - const Tag extends string, - Payload extends S.Top | S.Struct.Fields = typeof S.Void, - Success extends S.Top = typeof S.Void, - Error extends S.Top = typeof S.Never, - const Stream extends boolean = false, - Config extends GetContextConfig = {} - >(tag: Tag, options?: { - readonly payload?: Payload - readonly success?: Success - readonly error?: Error - readonly stream?: Stream - readonly config?: Config - readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? (( - payload: Payload extends S.Struct.Fields ? Simplify["Type"]> : Payload["Type"] - ) => string) - : never - }) => - & Rpc.Rpc< - Tag, - Payload extends S.Struct.Fields ? S.Struct : Payload, - Stream extends true ? RpcSchema.Stream : Success, - Stream extends true ? typeof S.Never : Error - > - & { readonly config: Config } - - middleware>( - ...mw: MWs - ): RecursiveHandleMWsSideways extends infer Res extends { - self: any - id: string - rcm: RequestContextMap - provided: keyof RequestContextMap - middlewares: ReadonlyArray - dmp: any - middlewareR: any - } ? MiddlewaresBuilder< - Res["self"], - Res["id"], - Res["rcm"], - Res["provided"], - Res["middlewares"], - Res["dmp"], - Res["middlewareR"] - > - : never - - // helps debugging what are the missing requirements (type only) - readonly [TypeTestId]: { - missingDynamicMiddlewares: Exclude - missingContext: MiddlewareR - } -} - -export type MiddlewaresBuilder< - Self, - Id extends string, - RequestContextMap extends Record, - Provided extends keyof RequestContextMap = never, - Middlewares extends ReadonlyArray = [], - DynamicMiddlewareProviders = unknown, - MiddlewareR extends { _tag: string } = never -> = - & BuildingMiddleware< - Self, - Id, - RequestContextMap, - Provided, - Middlewares, - DynamicMiddlewareProviders, - MiddlewareR - > - & // keyof Omit extends never is true when all the dynamic middlewares are provided - // MiddlewareR is never when all the required services from generic & dynamic middlewares are provided - (keyof Omit extends never ? [MiddlewareR] extends [never] ? MiddlewareMaker< - Self, - Id, - RequestContextMap, - Middlewares - > - : { new(_: never): {} } - : { new(_: never): {} }) - -const middlewareMaker = Effect.fnUntraced(function*< - MiddlewareProviders extends ReadonlyArray ->(middlewares: MiddlewareProviders) { - type Middleware = RpcMiddlewareV4< - MiddlewareMaker.ManyProvided, - MiddlewareMaker.ManyErrors, - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never - : Exclude, MiddlewareMaker.ManyProvided> - > - type Next = Parameters[0] - type Options = Parameters[1] - - // we want to run them in reverse order because latter middlewares will provide context to former ones - const reversed = middlewares.toReversed() - const context = yield* Effect.context() - - // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements - // v4: wrap middleware takes (effect, options) as two params instead of a single options bag - return (next: Next, options: Options) => { - // we start with the actual handler - let handler = next - - // inspired from Effect/RpcMiddleware - for (const tag of reversed) { - // use the tag to get the middleware from context - const middleware = Context.getUnsafe(context, tag) - - // wrap the current handler, allowing the middleware to run before and after it - handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe( - Effect.andThen(middleware(handler, options)) - ) as any - } - return handler - } -}) - -const makeMiddlewareBasic = () => -// by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys -< - const Id extends string, - RequestContextMap extends Record, - MiddlewareProviders extends ReadonlyArray ->( - id: Id, - rcm: RequestContextMap, - ...make: MiddlewareProviders -) => { - // reverse middlewares and wrap one after the other - const middleware = middlewareMaker(make) - - // Per-middleware error: union of the static `error` on the tag (if any) AND - // the rcm config entry pointed at by the middleware's `dynamic.key` (if any). - // Reason: middlewares declared with `dynamic: RequestContextMap.get("foo")` - // don't set a static `error` field — at runtime their `.error` defaults to - // `S.Never`. Without pulling from rcm, the composite middleware's - // `.error` collapses to `Never`, and `Rpc.exitSchema` (which walks - // `rpc.middlewares[*].error` to build the wire failure union) can't decode - // the actual middleware-thrown error type. Critical for stream rpcs whose - // top-level `errorSchema` is force-set to `Never` by effect-rpc. - const isMeaningfulError = (e: S.Top | undefined): e is S.Top => e !== undefined && e !== null && e !== S.Never - const rcmRecord = rcm as Record - const failures: Array = make.flatMap((_) => { - const out: Array = [] - if (isMeaningfulError(_.error)) out.push(_.error) - const key = _.dynamic?.key as string | undefined - if (key && rcmRecord[key] && isMeaningfulError(rcmRecord[key].error)) { - out.push(rcmRecord[key].error) - } - return out - }) - const provides = make.flatMap((_) => !_.provides ? [] : Array.isArray(_.provides) ? _.provides : [_.provides]) - const requires = make - .flatMap((_) => !_.requires ? [] : Array.isArray(_.requires) ? _.requires : [_.requires]) - .filter((_) => !provides.includes(_)) - - const [firstFailure, ...restFailures] = failures - - const MiddlewareMaker = RpcMiddlewareX.Tag()(id, { - error: (firstFailure - ? S.Union([firstFailure, ...restFailures]) - : S.Never) as unknown as MiddlewareMaker.ManyErrors extends never ? never - : S.Codec>, - requires: (requires.length > 0 - ? requires - : undefined) as unknown as Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never : [ - MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - ], - provides: (provides.length > 0 - ? provides - : undefined) as unknown as MiddlewareMaker.ManyProvided extends never ? never - : MakeTags> - }) - - const layer = Layer - .effect( - MiddlewareMaker, - middleware as Effect.Effect< - any - > - // todo; they dont change the type.. - // Effect.Error, - // Effect.Services - ) - - // add to the tag a default implementation - return Object.assign(MiddlewareMaker, { - layer, - // tag to be used to retrieve the RequestContextConfig from Rpc annotations - requestContext: Context.Service<"RequestContextConfig", GetContextConfig>( - "RequestContextConfig" - ), - requestContextMap: rcm - }) -} - -export const Tag = () => -< - const Id extends string, - RequestContextMap extends RequestContextMapTagAny ->(id: Id, rcm: RequestContextMap): MiddlewaresBuilder => { - const allMiddleware: MiddlewareMaker.Any[] = [] - const requestContext = Context.Service<"RequestContextConfig", GetContextConfig>( - "RequestContextConfig" - ) - const it = { - id, - // rpc with config - rpc: < - const Tag extends string, - Payload extends S.Top | S.Struct.Fields = typeof S.Void, - Success extends S.Top = typeof S.Void, - Error extends S.Top = typeof S.Never, - const Stream extends boolean = false, - Config extends GetContextConfig = {} - >(tag: Tag, options?: { - readonly payload?: Payload - readonly success?: Success - readonly error?: Error - readonly stream?: Stream - readonly config?: Config - readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? (( - payload: Payload extends S.Struct.Fields ? Simplify["Type"]> : Payload["Type"] - ) => string) - : never - }): - & Rpc.Rpc< - Tag, - Payload extends S.Struct.Fields ? S.Struct : Payload, - // TODO: enhance `Error`. type based on middleware config. - Stream extends true ? RpcSchema.Stream : Success, - Stream extends true ? typeof S.Never : Error - > - & { config: Config } => - { - const config = options?.config ?? {} as Config - - // The rpc's `error` schema carries ONLY the request's own declared errors. - // Middleware errors (rcm-derived) reach the wire via the middleware tag - // attached to the rpc group later (`RpcGroup.middleware(...)` at the - // routing/client level), and are unioned into the failure schema by - // `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. - // @ts-expect-error — TypeScript can't prove Simplify ≡ { [K in keyof T]: T[K] } for unresolved generics (primaryKey) - const rpc = Rpc.make(tag, { - ...options?.payload !== undefined ? { payload: options.payload } : {}, - ...options?.success !== undefined ? { success: options.success } : {}, - ...options?.error !== undefined ? { error: options.error } : {}, - ...options?.stream !== undefined ? { stream: options.stream } : {}, - ...options?.primaryKey !== undefined ? { primaryKey: options.primaryKey } : {} - }) as any - - return Object.assign(rpc.annotate(requestContext, config), { config }) - }, - middleware: (...middlewares: any[]) => { - for (const mw of middlewares) { - // recall that we run middlewares in reverse order - allMiddleware.unshift(mw) - } - return allMiddleware.filter((m) => !!m.dynamic).length !== Object.keys(rcm.config).length - // for sure, until all the dynamic middlewares are provided it's non sensical to call makeMiddlewareBasic - ? it - // actually, we don't know yet if MiddlewareR is never, but we can't easily check it at runtime - : Object.assign(makeMiddlewareBasic()(id, rcm.config, ...allMiddleware), it) - } - } - return it as any -} - -// alternatively consider group.serverMiddleware? hmmm -export const middlewareGroup = < - RequestContextMap extends Record, - Middleware extends RpcMiddlewareX.TagClassAny & { - readonly requestContext: RequestContextTag - readonly requestContextMap: RequestContextMap - } ->( - middleware: Middleware -) => -(group: RpcGroup.RpcGroup) => { - type RN = AddMiddleware - const middlewaredGroup = group.middleware(middleware) as unknown as RpcGroup.RpcGroup - const toLayerOriginal = middlewaredGroup.toLayer.bind(middlewaredGroup) - return Object.assign(middlewaredGroup, { - toLayerDynamic: < - Handlers extends HandlersFrom, - EX = never, - RX = never - >( - build: - | Handlers - | Effect.Effect - ): Layer.Layer< - Rpc.ToHandler, - EX, - | Exclude - | RpcMiddlewareX.HandlersContext - > => { - return toLayerOriginal(build as any) as any // ?? - } - }) -} diff --git a/packages/effect-app/src/rpc/README.md b/packages/effect-app/src/rpc/README.md deleted file mode 100644 index 192abc189..000000000 --- a/packages/effect-app/src/rpc/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Effect RPC extensions - -The extensions use V4 format of RPC middleware: - -- supports `requires` besides `provides` -- `requires` and `provides` should be set as second generic argument: `Tag`. -- `wrap: true` is the default, there is no classic `provides: Tag` - -## Features - -### Bundle Middleware - -Compose multiple middleware into a single middleware, so that you can use them easily together as a unit. -The middleware group is fully typesafe, supporting chained or sideways elimination of requirements. -All configured dynamic middleware must be provided or the bundle is not complete. - -### Dynamic Middleware - -Dynamic middleware is middleware that is being controlled by Configuration set on the RPC. -The middleware generally runs globally, but it's behaviour depends on configuration on the RPC. - -For instance, `AllowAnonymous`, with a default of `false`, would either provide a `UserProfile` service, or fail with a `NotLoggedInError`. -therefore `UserProfile` is eliminated from a handler implementing that rpc. -If the RPC however opts out and specifies: `allowAnonymous: true`, the `UserProfile` service is no longer eliminated from the handler, -and thus one has to use `Effect.serviceOption(UserProfile)` instead, to not get a type error. - -For instance, `RequireRoles` would by default require a `manager` role, but can be opted out from per RPC, perhaps lowering to `user` or `anonymous` role. - -### Create RPCGroups and Handlers from a common set of Middleware - -You create a bundle of middleware, containing standard or dynamic middleware, and from it you can create all your rpc groups and handlers. -NOTE: perhaps not as useful anymore if support for dynamic middleware gets integrated directly into effect RPC. -`middleware.group`, `middleware.rpc` and `group.toLayerDynamic` would become obsolete, and `RpcGroup.make().middleware(globalMiddleware)` would be simpler. - -## Examples - -See [tests](../../../infra/test/rpc-multi-middleware.test.ts) - -## Future - -Gauge interest to integrate natively into `@effect/rpc` package, and then integrate. diff --git a/packages/effect-app/src/rpc/RpcContextMap.ts b/packages/effect-app/src/rpc/RpcContextMap.ts deleted file mode 100644 index a3de0a89a..000000000 --- a/packages/effect-app/src/rpc/RpcContextMap.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { type AnyWithProps } from "effect/rpc/Rpc" -import type * as S from "effect/Schema" -import * as Context from "../Context.ts" -import { type RpcDynamic } from "./RpcMiddleware.ts" - -type Values> = T[keyof T] - -/** - * Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect Context. - * Unless explicitly configured as `true`. - */ -export type RpcContextMap = { - // todo; rename Provides - service: Service - error: E - contextActivation: true - inverted: false -} - -export declare namespace RpcContextMap { - /** - * Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect Context. - * Unless explicitly omitted. - */ - export type Inverted = { - service: Service - error: E - contextActivation: false - inverted: true - } - - export type Custom = { - service: Service - error: E - contextActivation: C - inverted: false - } - - export type Any = { - service: any - error: S.Top - contextActivation: any - inverted: boolean - } -} - -export type GetContextConfig> = { - [K in keyof RequestContextMap]?: RequestContextMap[K]["inverted"] extends true - ? RequestContextMap[K]["contextActivation"] extends true ? false - : RequestContextMap[K]["contextActivation"] extends false ? true - : RequestContextMap[K]["contextActivation"] - : RequestContextMap[K]["contextActivation"] -} - -export type GetEffectContext, T> = Values< - // inverted: contextActivation is false => remove if explicitly set to true (like allowAnonymous: true disables auth and auth service and related errors) - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends true ? never - : key extends keyof T ? T[key] extends true ? never : key - : key - ]: // TODO: or as an Optional available? - RequestContextMap[key]["service"] - } - // normal: contextActivation is true => add if explicitly set to true - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends false ? never - : key extends keyof T ? T[key] extends true ? key : never - : never - ]: // TODO: or as an Optional available? - RequestContextMap[key]["service"] - } -> - -export type GetEffectError, T> = Values< - // inverted: contextActivation is false => remove if explicitly set to true (like allowAnonymous: true disables auth and auth service and related errors) - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends true ? never - : key extends keyof T ? T[key] extends true ? never : key - : key - ]: // TODO: or as an Optional available? - RequestContextMap[key]["error"] - } - // normal: contextActivation is true => add if explicitly set to true - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends false ? never - : key extends keyof T ? T[key] extends true ? key : never - : never - ]: // TODO: or as an Optional available? - RequestContextMap[key]["error"] - } -> - -const tag = Context.Service("RequestContextConfig") - -export const makeMap = >(config: Config) => { - const cls = class { - readonly config: Config - constructor() { - this.config = config - } - } - return Object.assign(cls, { - config, /** Retrieves RequestContextConfig out of the Rpc annotations */ - getConfig: (rpc: AnyWithProps): GetContextConfig => { - return Context.getOrElse(rpc.annotations, tag as any, () => ({})) - }, - /** Adapter used when setting the dynamic prop on a middleware implementation */ - get: < - Key extends (keyof Config) & string - >(key: Key): RpcDynamic => ({ - key, - settings: { service: config[key]!["service"] } as Config[Key] - }) - }) -} - -export const make = () => -( - error: E -): RpcContextMap => ({ - service: null as Service, - error, - contextActivation: true, - inverted: false -}) - -export const makeInverted = () => -( - error: E -): RpcContextMap.Inverted => ({ - service: null as Service, - error, - contextActivation: false, - inverted: true -}) - -export const makeCustom = () => -( - error: E, - contextActivation: C -): RpcContextMap.Custom => ({ - service: null as Service, - error, - contextActivation, - inverted: false -}) - -export type RequestContextMapTagAny = { readonly config: Record } diff --git a/packages/effect-app/src/rpc/RpcMiddleware.ts b/packages/effect-app/src/rpc/RpcMiddleware.ts deleted file mode 100644 index d4090157e..000000000 --- a/packages/effect-app/src/rpc/RpcMiddleware.ts +++ /dev/null @@ -1,244 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { type NonEmptyReadonlyArray } from "effect/Array" -import type * as Effect from "effect/Effect" -import { type Rpc, RpcMiddleware } from "effect/rpc" -import { type TypeId } from "effect/rpc/RpcMiddleware" -import type * as S from "effect/Schema" -import type * as Scope from "effect/Scope" -import type * as Stream from "effect/Stream" -import type * as Context from "../Context.ts" -import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.ts" - -export type RpcMiddlewareV4 = RpcMiddleware.RpcMiddleware - -export type RpcOptionsOriginal = { - readonly optional?: boolean - readonly error?: S.Top - readonly requiredForClient?: boolean -} - -export type RpcDynamic = { - key: Key - settings: A -} - -export type AnyDynamic = { dynamic: RpcDynamic } - -export type DependsOn = { - readonly dependsOn: NonEmptyReadonlyArray | undefined -} - -export interface RpcOptionsDynamic extends RpcOptionsOriginal { - readonly dynamic: RpcDynamic - readonly dependsOn?: NonEmptyReadonlyArray | undefined -} - -export type Dynamic = Options extends RpcOptionsDynamic ? true : false - -export interface RpcMiddlewareDynamic extends RpcMiddleware.RpcMiddleware {} - -export interface TagClassAny extends RpcMiddleware.AnyService { - readonly optional: boolean - readonly provides: any - readonly requires: any - readonly error: S.Top - readonly dynamic?: RpcDynamic | undefined - readonly dependsOn?: NonEmptyReadonlyArray | undefined -} - -export declare namespace TagClass { - /** - * @since 1.0.0 - * @category models - */ - export type FailureSchema = Options extends { readonly error: S.Top; readonly optional?: false } - ? Options["error"] - // actually not, the Failure depends on Dynamic Middleware Configuration! - // : Options extends { readonly dynamic: RpcDynamic } ? A["error"] - : typeof S.Never - - /** - * @since 1.0.0 - * @category models - */ - export type Failure = Options extends { readonly error: S.Codec; readonly optional?: false } ? _A - // actually not, the Failure depends on Dynamic Middleware Configuration! - : Options extends { readonly dynamic: RpcDynamic } ? A["error"]["Type"] - : never - - /** - * @since 1.0.0 - * @category models - */ - // Avoid `S.Codec.DecodingServices = X extends Top ? X["DecodingServices"] : never`: - // tsgo fails to reduce it here and leaves `unknown`. `FailureSchema` is - // always `S.Top` (either `Options["error"]` or `typeof S.Never`), so read directly. - export type FailureContext = FailureSchema["DecodingServices"] - - /** - * @since 1.0.0 - * @category models - */ - export type FailureService = Optional extends true ? unknown : Failure - - /** - * @since 1.0.0 - * @category models - */ - export type Optional = Options extends { readonly optional: true } ? true : false - - /** - * @since 1.0.0 - * @category models - */ - export type RequiredForClient = Options extends { readonly requiredForClient: true } ? true : false - - /** - * @since 1.0.0 - * @category models - */ - export interface Base< - Self, - Name extends string, - Options, - Service, - Config extends { - requires?: any - provides?: any - } - > extends Context.Service { - new(_: never): Context.ServiceClass.Shape - readonly [TypeId]: TypeId - readonly optional: Optional - readonly error: FailureSchema - readonly "~ClientError": Options extends { readonly clientError: infer CE } ? CE : never - readonly provides: "provides" extends keyof Config ? Config["provides"] : never - readonly requires: "requires" extends keyof Config ? Config["requires"] : never - readonly dynamic: Options extends RpcOptionsDynamic ? Options["dynamic"] - : undefined - readonly dependsOn: Options extends DependsOn ? Options["dependsOn"] : undefined - readonly requiredForClient: RequiredForClient - } -} - -export interface TagClass< - Self, - Name extends string, - Options, - Config extends { - requires?: any - provides?: any - } = { requires: never; provides: never } -> extends - TagClass.Base< - Self, - Name, - Options, - Options extends RpcOptionsDynamic ? RpcMiddlewareDynamic< - TagClass.FailureService, - "requires" extends keyof Config ? Config["requires"] : never, - { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] } - > - : RpcMiddlewareV4< - "provides" extends keyof Config ? Config["provides"] : never, - TagClass.Failure, - "requires" extends keyof Config ? Config["requires"] : never - >, - Config - > -{} - -export const Tag = < - Self, - Config extends { - requires?: any - provides?: any - } = { requires: never; provides: never } ->() => -< - const Name extends string, - const Options extends RpcOptionsOriginal | RpcOptionsDynamic = {} ->( - id: Name, - options?: Options -): TagClass => - class extends RpcMiddleware.Service()(id, options as any) { - static readonly requires: "requires" extends keyof Config ? Config["requires"] : never - static readonly provides: "provides" extends keyof Config ? Config["provides"] : never - static readonly dynamic = options && "dynamic" in options ? options.dynamic : undefined - static readonly dependsOn = options && "dependsOn" in options ? options.dependsOn : undefined - } as any - -// not needed if there's official support in Rpc.Rpc. -export type AddMiddleware = R extends Rpc.Rpc< - infer _Tag, - infer _Payload, - infer _Success, - infer _Error, - infer _Middleware -> ? - & Rpc.Rpc< - _Tag, - _Payload, - _Success, - _Error, - _Middleware | Middleware - > - & { readonly config: R extends { readonly config: infer _C } ? _C : never } - : never - -// customized versions to handle dynamically eliminated context. -export type HandlersContext = keyof Handlers extends infer K - ? K extends keyof Handlers & string ? HandlerContext : never - : never - -export type HandlerContext = [Rpc.IsStream] extends - [true] ? Handler extends (...args: any) => - | Stream.Stream - | Rpc.Wrapper> - | Effect.Effect< - infer _A, - infer _EX, - infer _R - > - | Rpc.Wrapper< - Effect.Effect< - infer _A, - infer _EX, - infer _R - > - > ? Exclude, Scope.Scope> - : never - : Handler extends ( - ...args: any - ) => Effect.Effect | Rpc.Wrapper> - ? ExcludeProvides<_R, Rpcs, K> - : never - -// new -export type ExtractDynamicallyProvides = R extends - Rpc.Rpc ? _Middleware extends { - readonly requestContextMap: infer _RC - } ? _RC extends Record // ? GetEffectContext<_RC, { allowAnonymous: false }> - ? R extends { readonly config: infer _C } ? GetEffectContext<_RC, _C> - : GetEffectContext<_RC, {}> - : never - : never - : never - -export type ExtractProvides = R extends - Rpc.Rpc ? _Middleware extends { - readonly provides: infer _P - } ? [_P] extends [never] ? never - : _P /*_P extends Context.Service ? _I - : never */ - : never - : never - -export type ExcludeProvides = Exclude< - Env, - // customisation is down here. - ExtractProvides | ExtractDynamicallyProvides -> diff --git a/packages/effect-app/src/runtime.ts b/packages/effect-app/src/runtime.ts deleted file mode 100644 index a8b004539..000000000 --- a/packages/effect-app/src/runtime.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as Exit from "effect/Exit" -import { flow } from "effect/Function" -import * as Logger from "effect/Logger" -import * as ManagedRuntime from "effect/ManagedRuntime" -import { CauseException } from "./client/errors.ts" -import { type Context } from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" - -export const makeAppRuntime = Effect.fnUntraced(function*(layer: Layer.Layer) { - const l = layer.pipe( - Layer.provide(Logger.layer([Logger.consolePretty()])), - Layer.orDie - ) - const mrt = ManagedRuntime.make(l) - yield* mrt.contextEffect - return Object.assign(mrt, { - [Symbol.dispose]() { - return Effect.runSync(mrt.disposeEffect) - }, - - [Symbol.asyncDispose]() { - return mrt.dispose() - } - }) // as we initialise here, there is no more error left. -}) - -export function initializeSync(layer: Layer.Layer) { - const runtime = Effect.runSync(makeAppRuntime(layer)) - return runtime -} - -export function initializeAsync(layer: Layer.Layer) { - return Effect - .runPromise(makeAppRuntime(layer)) -} - -// we wrap into CauseException because we want to keep the full cause of the failure. -export const makeRunPromise = (services: Context) => - flow(Effect.runPromiseExitWith(services), (_) => - _.then( - Exit.match({ - onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")), - onSuccess: (value) => Promise.resolve(value) - }) - )) - -export const makeRunSync = (services: Context) => - flow( - Effect.runSyncExitWith(services), - Exit.match({ - onFailure: (cause) => { - throw new CauseException(cause, "runSync") - }, - onSuccess: (value) => value - }) - ) diff --git a/packages/effect-app/src/setupRequest.ts b/packages/effect-app/src/setupRequest.ts deleted file mode 100644 index a4c02eda7..000000000 --- a/packages/effect-app/src/setupRequest.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { SqlClient } from "effect/sql" -import * as Tracer from "effect/Tracer" -import { DataDependencyRecorder } from "./DataDependencies.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" -import * as Option from "./Option.ts" -import { LocaleRef, RequestContext, spanAttributes } from "./RequestContext.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import { NonEmptyString255 } from "./Schema.ts" -import { ContextMapContainer, storeId } from "./Store.ts" - -const withSqlTransaction = (self: Effect.Effect): Effect.Effect => - Effect.serviceOption(SqlClient.SqlClient).pipe( - Effect.flatMap(Option.match({ - onNone: () => self, - onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie) - })) - ) - -export const getRequestContext = Effect - .all({ - span: Effect.currentSpan.pipe(Effect.orDie), - locale: LocaleRef, - namespace: storeId - }) - .pipe( - Effect.map(({ locale, namespace, span }) => - RequestContext.make({ - span: Tracer.externalSpan(span), - locale, - namespace, - name: NonEmptyString255(span.name) - }) - ) - ) - -export const getRC = Effect.all({ - locale: LocaleRef, - namespace: storeId -}) - -const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => (f: Effect.Effect) => - Effect.andThen( - getRC, - (ctx) => - f.pipe( - Effect.withSpan(name, { - ...options, - attributes: { ...spanAttributes({ ...ctx, name: NonEmptyString255(name) }), ...options?.attributes } - }, { - captureStackTrace: options?.captureStackTrace ?? false - }), - // TODO: false - // request context info is picked up directly in the logger for annotations. - Effect.withLogSpan(name) - ) - ) - -export interface SetupRequestOptions { - readonly withTransaction?: boolean -} - -export const requestStateLayer = RequestScopedDependencies.layer(ContextMapContainer, DataDependencyRecorder) - -// Build `layer` against the ambient (request) scope rather than a sub-scope of the -// returned Effect. Required when the returned value is a streaming HttpServerResponse: -// the response body keeps producing chunks (and using layer-provided state) after the -// Effect returns, so a sub-scope would close too early and run finalizers mid-stream. -export const provideOnRequestScope = - (layer: Layer.Layer) => (self: Effect.Effect) => - Effect.gen(function*() { - const requestScope = yield* Effect.scope - // Fresh MemoMap per request: `Layer.buildWithScope` would otherwise reuse - // the ambient MemoMap living on the HTTP server fiber, sharing the built - // value (e.g. ContextMap) across every request handled by that server. - const memoMap = yield* Layer.makeMemoMap - const ctx = yield* Layer.buildWithMemoMap(layer, memoMap, requestScope) - return yield* Effect.provide(self, ctx) - }) - -export const setupRequestContextFromCurrent = - (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => (self: Effect.Effect) => - self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - Effect.provide(requestStateLayer, { local: true }) - ) - -// Streaming variant: binds ContextMapContainer to the ambient (request) scope so its -// finalizer (clear()) runs only after the response body is fully drained, not when the -// outer Effect returns its HttpServerResponse value. Use for handlers that return a -// streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context. -export const setupStreamingRequestContextFromCurrent = - (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => (self: Effect.Effect) => - self.pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - provideOnRequestScope(requestStateLayer) - ) - -// TODO: consider integrating Effect.withParentSpan -export function setupRequestContext( - self: Effect.Effect, - requestContext: RequestContext, - options?: SetupRequestOptions -) { - const layer = Layer.mergeAll( - requestStateLayer, - Layer.succeed(LocaleRef, requestContext.locale), - Layer.succeed(storeId, requestContext.namespace) - ) - return self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(requestContext.name), - Effect.provide(layer, { local: true }) - ) -} - -export function setupRequestContextWithCustomSpan( - self: Effect.Effect, - requestContext: RequestContext, - name: string, - options?: Tracer.SpanOptions & SetupRequestOptions -) { - const layer = Layer.mergeAll( - requestStateLayer, - Layer.succeed(LocaleRef, requestContext.locale), - Layer.succeed(storeId, requestContext.namespace) - ) - return self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - Effect.provide(layer, { local: true }) - ) -} diff --git a/packages/effect-app/src/toast.ts b/packages/effect-app/src/toast.ts deleted file mode 100644 index d9b674db4..000000000 --- a/packages/effect-app/src/toast.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as Context from "./Context.ts" -import { accessEffectFn } from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Option from "./Option.ts" - -export type ToastId = string | number -export type ToastOpts = { id?: ToastId; timeout?: number; groupId?: string; requestId?: string } -export type ToastOptsInternal = { id?: ToastId | null; timeout?: number; groupId?: string; requestId?: string } - -export type UseToast = () => { - error: (this: void, message: string, options?: ToastOpts) => ToastId - warning: (this: void, message: string, options?: ToastOpts) => ToastId - success: (this: void, message: string, options?: ToastOpts) => ToastId - info: (this: void, message: string, options?: ToastOpts) => ToastId - dismiss: (this: void, id: ToastId) => void -} - -export class CurrentToastId extends Context.Opaque()("CurrentToastId") {} - -/** fallback to CurrentToastId when available unless id is explicitly set to a value or null */ -export const wrap = (toast: ReturnType) => { - const wrap = (toastHandler: (message: string, options?: ToastOpts) => ToastId) => { - return (message: string, options?: ToastOptsInternal) => - Effect.serviceOption(CurrentToastId).pipe( - Effect.flatMap((currentToast) => - Effect.sync(() => { - const { id: _id, ...rest } = options ?? {} - const id = _id !== undefined - ? _id ?? undefined - : Option.getOrUndefined(Option.map(currentToast, (_) => _.toastId)) - // when id is undefined, we may end up with no toast at all.. - return toastHandler(message, id !== undefined ? { ...rest, id } : rest) - }) - ) - ) - } - return { - error: wrap(toast.error), - info: wrap(toast.info), - success: wrap(toast.success), - warning: wrap(toast.warning), - dismiss: (toastId: ToastId) => Effect.sync(() => toast.dismiss(toastId)) - } -} - -type ToastShape = ReturnType - -export class Toast extends Context.Opaque()("Toast") { - static readonly error = accessEffectFn(this, "error") - static readonly info = accessEffectFn(this, "info") - static readonly success = accessEffectFn(this, "success") - static readonly warning = accessEffectFn(this, "warning") - static readonly dismiss = accessEffectFn(this, "dismiss") -} diff --git a/packages/effect-app/src/transform.ts b/packages/effect-app/src/transform.ts deleted file mode 100644 index 0855cb3a0..000000000 --- a/packages/effect-app/src/transform.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { NonEmptyReadonlyArray } from "effect/Array" -import * as Option from "effect/Option" -import type { Misc, Union } from "ts-toolbelt" -import type * as SET from "./Set.ts" - -// type SomeObject = { -// 0: Option.Option -// a: { -// b: Option.Option -// g: Option.Option> -// h: Option.Option<{ i: Option.Option }> -// } -// c: { d: Array }>> } -// } -// type test0 = Transform -// type test1 = Transform - -type OptOf = Union.Exclude< - A extends Option.Some ? X | null : A, - Option.None -> -// eslint-disable-next-line @typescript-eslint/no-explicit-any - -export type TransformRoot = O extends Option.Option ? Transform> - : Transform -export type Transform = O extends Misc.BuiltIn | Misc.Primitive ? O - : { - [K in keyof O]: OptOf extends infer X ? X extends (infer Y)[] ? OptOf>[] - : X extends NonEmptyReadonlyArray ? NonEmptyReadonlyArray>> - : X extends SET.Set ? SET.Set>> - : X extends readonly (infer Y)[] ? readonly OptOf>[] - : Transform - : never - } - -export const encodeOptsAsNullable = (root: T): TransformRoot => encodeOptsAsNullable_(root, new Map()) - -const encodeOptsAsNullable_ = (value: any, cacheMap: Map): any => { - const cacheEntry = cacheMap.get(value) - if (cacheEntry) { - return cacheEntry - } - - if (Array.isArray(value)) { - const newAr: typeof value = [] - cacheMap.set(value, newAr) - value.forEach((x) => newAr.push(encodeOptsAsNullable_(x, cacheMap))) - return newAr - } - - if ( - value instanceof Date - || typeof value === "function" - || (typeof value === "object" && value !== null && "then" in value && typeof value.then === "function") - ) { - return value - } - - if (value instanceof Set) { - const newValue = [...value] - cacheMap.set(value, newValue) - return newValue - } - - if (value instanceof Object) { - if (value._tag === "Some" || value._tag === "None") { - const v = value as Option.Option - return encodeOptsAsNullable_(Option.getOrNull(v), cacheMap) - } - const newObj = {} as Record - cacheMap.set(value, newObj) - - Object.keys(value).forEach((key) => { - newObj[key] = encodeOptsAsNullable_(value[key], cacheMap) - }) - return newObj - } - return value -} diff --git a/packages/effect-app/src/utils.ts b/packages/effect-app/src/utils.ts deleted file mode 100644 index ed4613820..000000000 --- a/packages/effect-app/src/utils.ts +++ /dev/null @@ -1,901 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-function-type */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-redundant-type-constituents */ -import * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as Exit from "effect/Exit" -import * as Fiber from "effect/Fiber" -import { dual } from "effect/Function" -import * as Option from "effect/Option" -import { isFunction } from "effect/Predicate" -import * as Record from "effect/Record" -import * as Result from "effect/Result" -import { identity, pipe } from "./Function.ts" -import type { DeepMutable, Equals, Mutable } from "./Types.ts" - -// codegen:start {preset: barrel, include: ./utils/*.ts, nodir: false } -export * from "./utils/effectify.ts" -export * from "./utils/extend.ts" -export * from "./utils/gen.ts" -export * from "./utils/logger.ts" -export * from "./utils/logLevel.ts" -// codegen:end - -export * from "effect/Utils" - -export const cloneTrait = Symbol.for("clone-trait") -export interface Clone { - [cloneTrait](this: this, that: any): this -} - -export const unsafeRight = (ei: Result.Result) => { - if (Result.isFailure(ei)) { - console.error(ei.failure) - throw ei.failure - } - return ei.success -} - -export const unsafeSome = (makeErrorMessage: () => string) => (o: Option.Option) => { - if (Option.isNone(o)) { - throw new Error(makeErrorMessage()) - } - return o.value -} - -export function toString(v: unknown) { - return `${v}` -} - -export const typedKeysOf = (obj: T) => Object.keys(obj) as (keyof T)[] -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -export const typedValuesOf = (obj: T) => Object.values(obj) as ValueOf[] -type ValueOf = T[keyof T] - -export type Constructor = { new(...args: any[]): T } -export type ThenArg = T extends Promise ? U - : T extends (...args: any[]) => Promise ? V - : T - -/** - * @deprecated TODO: does not filter optional fields! - */ -type NoUndefinedField = { [P in keyof T]: Exclude } -export function dropUndefinedT>( - input: A -): NoUndefinedField { - const newR = pipe( - input, - Record.filter((x): x is A => x !== undefined) - ) - return newR as any -} - -type IsOptional, K extends keyof T> = {} extends Pick ? true : false - -type WithUndefined> = { - [K in keyof T]: IsOptional extends true ? T[K] | undefined : T[K] -} - -type WithUndefinedRecursive> = { - [K in keyof T]: WithUndefinedRecursive extends infer $RTK - ? IsOptional extends true ? $RTK | undefined : $RTK - : never -} - -/** a version of @see dropUndefinedT that keeps support for go to definition, auto completion, and documentation of fields! */ -export function dropUndefinedT2>() { - return ( - input: WithUndefined - ): Desired => { - const newR = pipe( - input as any, - Record.filter((x): x is Desired => x !== undefined) - ) - return newR as any - } -} - -/** - * A recursive version of @see dropUndefinedT that preserves IDE support for - * go-to-definition, auto-completion, and field documentation. - * - * Note: This utility does not physically remove `undefined` fields at runtime. - * It provides type-level compatibility with libraries that use `prop?: type` - * syntax. Before the introduction of `exactOptionalPropertyTypes`, this syntax - * implicitly allowed `undefined` values. Libraries that adopted `prop?: type` - * before this flag was introduced still accept `undefined` at runtime. - */ - -export function dropUndefinedRec>() { - return ( - input: WithUndefinedRecursive - ): Desired => { - // doesn't drop for real - return input as any - } -} - -export type Dictionary = { - readonly [P in string]: T -} - -export function dropUndefined( - input: Dictionary -): Dictionary { - const newR = pipe( - input, - Record.filter((x): x is A => x !== undefined) - ) - return newR -} - -type GetTag = T extends { _tag: infer K } ? K : never -export const isOfType = (tag: GetTag) => (e: { _tag: string }): e is T => e._tag === tag - -export function capitalize(string: T): Capitalize { - return (string.charAt(0).toUpperCase() + string.slice(1)) as Capitalize -} - -export function uncapitalize(string: T): Uncapitalize { - return (string.charAt(0).toLowerCase() + string.slice(1)) as Uncapitalize -} - -export function pretty(o: unknown): string { - return JSON.stringify(o, undefined, 2) ?? "undefined" -} - -export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void ? I - : never - -// generally A | B is not assignable to A & B -export type IsUnion = [T] extends [UnionToIntersection] ? false : true - -export type EnforceNonEmptyRecord = keyof R extends never ? never : R - -export function intersect( - ...as: AS -): UnionToIntersection<{ [k in keyof AS]: AS[k] }[number]> { - return as.reduce((a: any, b: any) => Object.assign(a, b), {}) as any -} - -export type IsEqualTo = (() => T extends X ? 1 : 2) extends < - T ->() => T extends Y ? 1 : 2 ? true - : false - -export const unifyIndex = Symbol() -export type unifyIndex = typeof unifyIndex - -export interface UnifiableIndexed<_X> {} -export type UnifiableIndexedURI = keyof UnifiableIndexed - -export interface Unifiable { - // Sync: [X] extends [Sync] ? Sync : never - // Effect: [X] extends [Effect] - // ? [X] extends [Sync] - // ? never - // : Effect.Effect - // : never - Unify: [X] extends [{ readonly [unifyIndex]: infer K }] ? K extends UnifiableIndexedURI ? UnifiableIndexed[K] - : never - : never -} - -export type Unify = Unifiable[keyof Unifiable] extends never ? X - : Unifiable[keyof Unifiable] - -// forked from https://github.com/Alorel/typescript-lazy-get-decorator - -type DecoratorReturn = PropertyDescriptor | NewDescriptor - -function decorateNew( - inp: NewDescriptor, - setProto: boolean, - makeNonConfigurable: boolean, - resultSelector: ResultSelectorFn -): NewDescriptor { - const out: NewDescriptor = Object.assign({}, inp) - if (out.descriptor) { - out.descriptor = Object.assign({}, out.descriptor) - } - const actualDesc: PropertyDescriptor = out.descriptor || /* istanbul ignore next */ out - - const originalMethod = validateAndExtractMethodFromDescriptor(actualDesc) - const isStatic = inp.placement === "static" - - actualDesc.get = function(this: any): any { - return getterCommon( - isStatic ? this : Object.getPrototypeOf(this), - out.key, - isStatic, - !!actualDesc.enumerable, - originalMethod, - this, - // eslint-disable-next-line prefer-rest-params - arguments, - setProto, - makeNonConfigurable, - resultSelector - ) - } - - return out -} - -function decorateLegacy( - target: any, - key: PropertyKey, - descriptor: PropertyDescriptor | undefined, - setProto: boolean, - makeNonConfigurable: boolean, - // tslint:enable:bool-param-default - resultSelector: ResultSelectorFn -): PropertyDescriptor { - /* istanbul ignore if */ - if (!descriptor) { - descriptor = Object.getOwnPropertyDescriptor(target, key) - if (!descriptor) { - const e = Object.assign(new Error("@LazyGetter is unable to determine the property descriptor"), { - $key: key, - $target: target - }) - throw e - } - } - - const originalMethod = validateAndExtractMethodFromDescriptor(descriptor) - - return Object.assign({}, descriptor, { - get(this: any): any { - return getterCommon( - target, - key, - Object.getPrototypeOf(target) === Function.prototype, - !!descriptor.enumerable, - originalMethod, - this, - // eslint-disable-next-line prefer-rest-params - arguments, - setProto, - makeNonConfigurable, - resultSelector - ) - } - }) -} - -/** Signifies that the modified property descriptor can be reset to its original state */ -interface ResettableDescriptor { - /** - * Restore the property descriptor on the given class instance or prototype and re-apply the lazy getter. - * @param on The class instance or prototype - */ - reset(on: any): void -} - -/** ES7 proposal descriptor, tweaked for Babel */ -interface NewDescriptor extends PropertyDescriptor { - descriptor?: PropertyDescriptor - - key: PropertyKey - - kind: string - - placement: string -} - -/** A filter function that must return true for the value to cached */ -export type ResultSelectorFn = (v: any) => boolean - -function defaultFilter(): boolean { - return true -} - -function validateAndExtractMethodFromDescriptor(desc: PropertyDescriptor): Function { - // eslint-disable-next-line @typescript-eslint/unbound-method - const originalMethod = desc.get - - if (!originalMethod) { - throw new Error("@LazyGetter can only decorate getters!") - } else if (!desc.configurable) { - throw new Error("@LazyGetter target must be configurable") - } - - return originalMethod -} - -function getterCommon( // tslint:disable-line:parameters-max-number - target: any, - key: PropertyKey, - isStatic: boolean, - enumerable: boolean, - originalMethod: Function, - thisArg: any, - args: IArguments, - setProto: boolean, - makeNonConfigurable: boolean, - resultSelector: ResultSelectorFn -): any { - const value = originalMethod.apply(thisArg, args) - - if (resultSelector(value)) { - const newDescriptor: PropertyDescriptor = { - configurable: !makeNonConfigurable, - enumerable, - value - } - - if (isStatic || setProto) { - Object.defineProperty(target, key, newDescriptor) - } - - if (!isStatic) { - Object.defineProperty(thisArg, key, newDescriptor) - } - } - - return value -} - -/** - * Evaluate the getter function and cache the result - * @param [setProto=false] Set the value on the class prototype as well. Only applies to non-static getters. - * @param [makeNonConfigurable=false] Set to true to make the resolved property non-configurable - * @param [resultSelector] A filter function that must return true for the value to cached - * @return A decorator function - */ -export function LazyGetter( - setProto = false, - makeNonConfigurable = false, - resultSelector: ResultSelectorFn = defaultFilter -): MethodDecorator & ResettableDescriptor { - let desc: PropertyDescriptor - let prop: PropertyKey - let args: [targetOrDesc: any, key: PropertyKey | undefined, descriptor: PropertyDescriptor | undefined] | undefined - let isLegacy: boolean - - function decorator( - targetOrDesc: any, - key: PropertyKey | undefined, - descriptor: PropertyDescriptor | undefined - ): DecoratorReturn { - args = [targetOrDesc, key, descriptor] - if (key === undefined) { - if (typeof desc === "undefined") { - const newDescriptor = targetOrDesc as NewDescriptor - isLegacy = false - prop = newDescriptor.key - desc = Object.assign( - {}, - newDescriptor.descriptor - /* istanbul ignore next */ || targetOrDesc - ) - } - - return decorateNew(targetOrDesc, setProto, makeNonConfigurable, resultSelector) - } else { - if (typeof desc === "undefined") { - isLegacy = true - prop = key - desc = Object.assign( - {}, - descriptor - /* istanbul ignore next */ || Object.getOwnPropertyDescriptor( - targetOrDesc, - key - ) - ) - } - - return decorateLegacy( - targetOrDesc, - key, - descriptor, - setProto, - makeNonConfigurable, - resultSelector - ) - } - } - - decorator.reset = setProto - ? thrower - : (on: any): void => { - if (!on) { - throw new Error("Unable to restore descriptor on an undefined target") - } - if (!desc) { - throw new Error( - "Unable to restore descriptor. Did you remember to apply your decorator to a method?" - ) - } - if (!args) { - throw new Error("Unable to restore descriptor before applying the decorator.") - } - // Restore descriptor to its original state - Object.defineProperty(on, prop, desc) - const ret = decorator.apply(null, args) - const restoredDescriptor = isLegacy ? ret : "descriptor" in ret && ret.descriptor ? ret.descriptor : ret - Object.defineProperty(on, prop, restoredDescriptor) - } - - return decorator -} - -function thrower(): never { - throw new Error("This decoration modifies the class prototype and cannot be reset.") -} - -export type RefinementWithIndex = (i: I, a: A) => a is B - -export type PredicateWithIndex = (i: I, a: A) => boolean - -export type Erase = R & K extends K & infer R1 ? R1 : R - -/** from ts-toolbelt, minimal port of Compute */ - -export type Depth = "flat" | "deep" - -type Errors = Error -// | EvalError -// | RangeError -// | ReferenceError -// | SyntaxError -// | TypeError -// | URIError - -type Numeric = - // | Number - // | BigInt // not needed - // | Math - Date - -type Textual = - // | String - RegExp - -type Arrays = - // | Array - // | ReadonlyArray - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array -// | BigInt64Array -// | BigUint64Array - -type Maps = - // | Map - // | Set - | ReadonlyMap - | ReadonlySet - | WeakMap - | WeakSet - -type Structures = - | ArrayBuffer - // | SharedArrayBuffer - // | Atomics - | DataView -// | JSON - -type Abstractions = Function | Promise | Generator -// | GeneratorFunction - -type WebAssembly = never - -export type BuiltInObject = - | Errors - | Numeric - | Textual - | Arrays - | Maps - | Structures - | Abstractions - | WebAssembly - -export type ComputeRaw = A extends Function ? A - : - & { - [K in keyof A]: A[K] - } - & {} - -export type ComputeFlat = A extends BuiltInObject ? A - : - & { - [K in keyof A]: A[K] - } - & {} - -export type ComputeDeep = A extends BuiltInObject ? A - : - & { - [K in keyof A]: ComputeDeep - } - & {} - -export type Compute = { - flat: ComputeFlat - deep: ComputeDeep -}[depth] - -///// - -export const LazySymbol = Symbol("lazy") - -interface Lazy { - [LazySymbol]: Record -} - -export function lazyGetter(creator: (target: T) => T2) { - const key = Symbol(creator.name) - const f = (target: T): T2 => { - let lazy = (target as unknown as Lazy)[LazySymbol] - if (!lazy) { - lazy = {} - Object.defineProperty(target, LazySymbol, { enumerable: false, value: lazy }) - } else if (lazy[key]) { - return lazy[key] - } - const value = creator(target) - lazy[key] = value - return value - } - Object.defineProperty(f, "name", { - enumerable: false, - value: `Lazy<${creator.name}>` - }) - return f -} - -export function exhaustiveMatch() { - return any>>(handlers: Out) => (t: T): ReturnType => handlers[t](t) -} - -export function exhaustiveMatch_(t: T) { - return any>>(handlers: Out): ReturnType => handlers[t](t) -} - -export function assertUnreachable(x: never): never { - throw new Error("Unknown case " + x) -} - -export type OptPromise any> = ( - ...args: Parameters -) => Promise> | ReturnType - -export function access(t: Record) { - return (key: T) => t[key] -} - -export function todayAtUTCNoon() { - const localDate = new Date() - const utcDateAtNoon = Date.UTC( - localDate.getFullYear(), - localDate.getMonth(), - localDate.getDate(), - 12 - ) - return new Date(utcDateAtNoon) -} - -function anyOp_$(self: T) { - return { - get subject() { - return self - } - } -} - -export function anyOp$(self: T): AnyOps { - return anyOp_$(self) -} - -export interface AnyOps { - subject: T -} - -export const clone = dual< - (f: NoInfer) => (self: A) => A, - (self: A, f: A) => A ->(2, (self, f) => { - if (cloneTrait in (self as any)) { - const selfWithClone = self as typeof self & Clone - return selfWithClone[cloneTrait](f) - } - return Object.setPrototypeOf(f, Object.getPrototypeOf(self)) as typeof self -}) - -export const copy = dual< - { - (f: (a: A) => Partial>): (self: A) => A - (f: Partial>): (self: A) => A - }, - { - (self: A, f: (a: A) => Partial): A - (self: A, f: Partial): A - } ->(2, (self: A, f: Partial | ((a: A) => Partial)) => clone(self, { ...self, ...(isFunction(f) ? f(self) : f) })) - -export type CopyOriginU any> = - & { - [K in keyof U & keyof InstanceType]?: U[K] - } - & {} - -export type CopyOriginRet = - & { - [K in keyof A | keyof U]: K extends keyof U ? U[K] : A[K & keyof A] - } - & {} - -export type CopyOriginSelf = Equals<{}, U> extends true - ? Equals extends true ? `updates argument is empty or contains only extra properties` - : A - : A - -export interface StructuralCopyOrigin { - >( - f: (a: A) => - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): (self: CopyOriginSelf) => CopyOriginRet - >( - updates: - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): (self: CopyOriginSelf) => CopyOriginRet - >( - self: CopyOriginSelf, - f: (a: A) => - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): CopyOriginRet - >( - self: CopyOriginSelf, - updates: - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): CopyOriginRet -} - -// just one input param: the convention is that the ctor takes an object -// containing the properties of the value (I can't put object there as type because of contravariance) -/** - * By design this does not return `Self` directly. - * - * The return type is computed from `Self` and the update payload so callers can - * expose an explicit structural return type that remains assignable to `Self`. - */ -export const copyOrigin = any>(ctor: Ctor) => - dual< - { - , U extends Partial>>( - f: (a: A) => CopyOriginU - ): (self: CopyOriginSelf) => CopyOriginRet - , U extends Partial>>( - updates: CopyOriginU - ): (self: CopyOriginSelf) => CopyOriginRet - }, - { - , U extends Partial>>( - self: CopyOriginSelf, - f: (a: A) => CopyOriginU - ): CopyOriginRet - , U extends Partial>>( - self: CopyOriginSelf, - updates: CopyOriginU - ): CopyOriginRet - } - >( - 2, - , U extends Partial>>( - self: Equals<{}, U> extends true ? `updates argument is empty or contains only extra properties` : A, - f: - | CopyOriginU - | ((a: A) => CopyOriginU) - ): CopyOriginRet => { - const o = { ...self, ...(isFunction(f) ? f(self as any) : f) } - - if (cloneTrait in (self as any)) { - const selfWithClone = self as typeof self & Clone - return selfWithClone[cloneTrait](o) - } - - return new ctor(o) - } - ) - -export function debug(a: AnyOps, name: string) { - let r: string | A = a.subject - try { - r = pretty(a.subject) - } catch { /* empty */ } - return Effect - .logDebug("print") - .pipe( - Effect.annotateLogs(name, `${r}`), - Effect - .map(() => a.subject) - ) -} - -export function debugUnsafe(a: AnyOps, name: string) { - console.log(name, a.subject) - return a.subject -} - -export function spread< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Fields extends Record, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - NewProps ->(fields: Fields, fnc: (fields: Fields) => NewProps) { - return fnc(fields) -} - -export function spreadS< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Fields extends Record ->(fields: Fields, fnc: (fields: Fields) => Fields) { - return fnc(fields) -} - -export function makeAzureFriendly(path: string) { - return path.replace(/\//g, "___SL@SH___") -} - -export function undoAzureFriendly(path: T): T { - return path.replace(/___SL@SH___/g, "/") as T -} - -export function arrayMove( - arrInput: readonly T[], - oldIndex: number, - newIndex: number -) { - const arr: (T | undefined)[] = [...arrInput] - while (oldIndex < 0) { - oldIndex += arr.length - } - while (newIndex < 0) { - newIndex += arr.length - } - if (newIndex >= arr.length) { - let k = newIndex - arr.length + 1 - while (k--) { - arr.push(undefined) - } - } - arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]) - return arr -} - -export function arrayMoveDropUndefined( - arrInput: readonly (T | undefined)[], - oldIndex: number, - newIndex: number -): T[] { - return arrayMove(arrInput, oldIndex, newIndex).filter((x): x is T => x !== undefined) -} - -export function arMoveElDropUndefined(el: T, newIndex: number) { - return (arrInput: ReadonlyArray): Option.Option> => { - const ar = [...arrInput] - const index = ar.findIndex((x) => x === el) - if (index === -1) { - return Option.none() - } - return Option.some(arrayMoveDropUndefined(ar, index, newIndex)) - } -} - -export function setMoveElDropUndefined(el: T, newIndex: number) { - return (arrInput: ReadonlySet): Option.Option> => - pipe([...arrInput], arMoveElDropUndefined(el, newIndex), Option.map((ar) => new Set(ar))) -} - -type RemoveNonArray = T extends readonly any[] ? T : never -export function isNativeTuple(a: A): a is RemoveNonArray { - return Array.isArray(a) -} - -export const mutable: { (a: A, deep: true): DeepMutable; (a: A): Mutable } = identity - -/** - * Recursively removes all elements assignable to type `E` from tuple `T`. - * - * @example - * ```typescript - * type Mixed = [1, "hello", 2, "world", 3] - * type Numbers = ExcludeFromTuple - * // Result: [1, 2, 3] - * ``` - */ -export type ExcludeFromTuple = T extends [infer F, ...infer R] - ? [F] extends [E] ? ExcludeFromTuple - : [F, ...ExcludeFromTuple] - : [] - -export const addAbortToRuntimeFiber = (fiber: Fiber.Fiber, signal: AbortSignal) => { - const abort = () => Effect.runSync(Fiber.interrupt(fiber)) - if (signal.aborted) { - abort() - return fiber - } - signal.addEventListener("abort", abort) - return fiber -} - -export const runtimeFiberAsPromise = (fiber: Fiber.Fiber, signal?: AbortSignal) => { - if (signal) addAbortToRuntimeFiber(fiber, signal) - return new Promise((resolve, reject) => - fiber.addObserver((exit) => { - if (Exit.isSuccess(exit)) { - resolve(exit.value) - } else { - // eslint-disable-next-line - reject(Cause.squash(exit.cause)) - } - }) - ) -} - -// unifies any input to be an effect. -export const wrapEffect = >( - m: ((...args: Args) => A) | ((...args: Args) => Effect.Effect) | I -) => { - if (typeof m === "function") { - return (...args: Args): Effect.Effect => { - const r = (m as any)(...args) - if (Effect.isEffect(r)) return r as Effect.Effect - return Effect.succeed(r) - } - } - return (): Effect.Effect => Effect.succeed(m) -} - -export type UnionToTuples = [T] extends [never] ? [] - : T extends any ? - | [T, ...UnionToTuples>] - | UnionToTuples> - : [] - -const genConstructor = (function*() {}).constructor - -/** - * @example - * ```ts - * import * as Utils from "effect/Utils" - * - * function* generatorFn() { - * yield 1 - * yield 2 - * } - * - * console.log(Utils.isGeneratorFunction(generatorFn)) // true - * console.log(Utils.isGeneratorFunction(() => {})) // false - * ``` - * - * @category predicates - * @since 3.11.0 - */ -export const isGeneratorFunction = (u: unknown): u is (...args: Array) => Generator => - isFunction(u) && u.constructor === genConstructor diff --git a/packages/effect-app/src/utils/effectify.ts b/packages/effect-app/src/utils/effectify.ts deleted file mode 100644 index 2aab5cad9..000000000 --- a/packages/effect-app/src/utils/effectify.ts +++ /dev/null @@ -1,260 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -import * as Effect from "effect/Effect" - -interface Callback { - (err: E, a?: A): void -} - -type ArgsWithCallback, E, A> = [...args: Args, cb: Callback] - -type WithoutNull = unknown extends A ? void : Exclude - -type Effectify = T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - (...args: ArgsWithCallback): infer _R10 -} ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - (...args: Args9): Effect.Effect, E> - (...args: Args10): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - (...args: Args9): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - } ? { - (...args: Args1): Effect.Effect, E> - } - : never - -type CallbackError = T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - (...args: ArgsWithCallback): infer _R10 -} ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - } ? NonNullable - : never - -export const effectify: { - ) => any>(fn: F): Effectify> - ) => any, E>( - fn: F, - onError: (error: CallbackError, args: Parameters) => E - ): Effectify - ) => any, E, E2>( - fn: F, - onError: (error: CallbackError, args: Parameters) => E, - onSyncError: (error: unknown, args: Parameters) => E2 - ): Effectify -} = - // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type - ((fn: Function, onError?: (e: any, args: any) => any, onSyncError?: (e: any, args: any) => any) => - (...args: Array) => - Effect.callback((resume) => { - try { - fn(...args, (err: Error | null, result: A) => { - if (err) { - resume(Effect.fail(onError ? onError(err, args) : err)) - } else { - resume(Effect.succeed(result)) - } - }) - } catch (err) { - resume(onSyncError ? Effect.fail(onSyncError(err, args)) : Effect.die(err)) - } - })) as any diff --git a/packages/effect-app/src/utils/extend.ts b/packages/effect-app/src/utils/extend.ts deleted file mode 100644 index b956b3781..000000000 --- a/packages/effect-app/src/utils/extend.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Own-property define that shadows getter-only prototype fields such as - * Effect 4 `Schema.make`. `Object.assign` uses [[Set]] and cannot. - */ -export const defineOwn = (target: object, key: PropertyKey, value: unknown) => { - Object.defineProperty(target, key, { - value, - writable: true, - enumerable: true, - configurable: true - }) -} - -const assignOwn = (target: T, source: object): T => { - for (const key of Reflect.ownKeys(source)) { - const desc = Object.getOwnPropertyDescriptor(source, key) - if (!desc?.enumerable) continue - defineOwn(target, key, (source as Record)[key]) - } - return target -} - -export const extend = (a: T, ext: X) => { - assignOwn(a, ext) - return a as T & X -} - -export const extendM = (a: T, ext: (a: T) => X) => extend(a, ext(a)) diff --git a/packages/effect-app/src/utils/gen.ts b/packages/effect-app/src/utils/gen.ts deleted file mode 100644 index e2bae01ce..000000000 --- a/packages/effect-app/src/utils/gen.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { type Effect } from "effect/Effect" - -export namespace EffectGenUtils { - export type Success = EG extends Effect ? A - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? A - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer A, infer _2> ? A - : never - - export type Error = EG extends Effect ? E - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? never - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer _A, infer _2> ? E - : never - - export type Context = EG extends Effect ? R - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? never - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer _A, infer _2> ? R - : never -} diff --git a/packages/effect-app/src/utils/logLevel.ts b/packages/effect-app/src/utils/logLevel.ts deleted file mode 100644 index dba7d1e27..000000000 --- a/packages/effect-app/src/utils/logLevel.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type * as LogLevel from "effect/LogLevel" - -export const LogLevelToSentry = (level: LogLevel.LogLevel) => { - switch (level) { - case "Debug": - return "debug" as const - case "Info": - return "info" as const - case "Warn": - return "warning" as const - case "Error": - return "error" as const - case "Fatal": - return "fatal" as const - } - return "log" as const -} diff --git a/packages/effect-app/src/utils/logger.ts b/packages/effect-app/src/utils/logger.ts deleted file mode 100644 index 68c0f9ab6..000000000 --- a/packages/effect-app/src/utils/logger.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Effect from "effect/Effect" -import type * as LogLevel from "effect/LogLevel" -import * as Context from "../Context.ts" - -type Levels = "info" | "debug" | "warn" | "error" - -export class LogLevels extends Context.Reference("LogLevels", { - defaultValue: () => new Map() -}) {} - -export const makeLog = (namespace: string, defaultLevel: Levels = "warn") => { - const level = LogLevels.use((levels) => Effect.succeed(levels.get(namespace) ?? defaultLevel)) - const withLogNamespace = Effect.annotateLogs({ logNamespace: namespace }) - return { - logWarning: (...message: ReadonlyArray) => - Effect.flatMap(level, (l) => - l === "info" || l === "debug" || l === "warn" - ? Effect.logWarning(...message).pipe(withLogNamespace) - : Effect.void), - logError: (...message: ReadonlyArray) => Effect.logError(...message).pipe(withLogNamespace), - logFatal: (...message: ReadonlyArray) => Effect.logFatal(...message).pipe(withLogNamespace), - logInfo: (...message: ReadonlyArray) => - Effect.flatMap( - level, - (l) => l === "info" || l === "debug" ? Effect.logInfo(...message).pipe(withLogNamespace) : Effect.void - ), - logDebug: (...message: ReadonlyArray) => - Effect.flatMap(level, (l) => l === "debug" ? Effect.logDebug(...message).pipe(withLogNamespace) : Effect.void), - // for now always log - logWithLevel: (level: LogLevel.Severity, ...message: ReadonlyArray) => - Effect.logWithLevel(level)(...message).pipe(withLogNamespace) - } -} diff --git a/packages/effect-app/src/validation.ts b/packages/effect-app/src/validation.ts deleted file mode 100644 index 24f9ade4b..000000000 --- a/packages/effect-app/src/validation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Predicate } from "./Function.ts" - -export const all_ = (v: T, ...a: Predicate[]) => !a.some((x) => !x(v)) -export const all = (...a: Predicate[]) => (v: T) => all_(v, ...a) - -export const maxN = (max: number) => (v: number) => v <= max -export const minN = (min: number) => (v: number) => v >= min - -export const max = (max: number) => { - const f = maxN(max) - return (v: { length: number }) => f(v.length) -} -export const min = (min: number) => { - const f = minN(min) - return (v: { length: number }) => f(v.length) -} - -export * from "./validation/validators.ts" diff --git a/packages/effect-app/src/validation/validators.ts b/packages/effect-app/src/validation/validators.ts deleted file mode 100644 index f2ec83f69..000000000 --- a/packages/effect-app/src/validation/validators.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Not importing from /es/lib because they are .js files and bundlers probably assume they're cjs :( -import isEmail, { type IsEmailOptions } from "validator/lib/isEmail.js" - -// Source https://emailregex.com/ -// eslint-disable-next-line no-control-regex -// const EMAIL_REGEX = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/ - -// allow for "/", "+", "-", " .js" and digits -const PHONE_REGEX = /^\+?[\d-/ ]*$/ - -const PHONE_LENGTH = 7 - -/** - * Validate emails according to RFC 5322 - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isValidEmail = isEmail as any as (str: string, options?: IsEmailOptions) => boolean - -/** - * Validates that phone number contains at least 7 numbers. - * Allowed characters are [0-1] "/", "+", "-", " .js" - */ -export const isValidPhone = (phone: string): boolean => { - const isValidPhone = PHONE_REGEX.test(phone) - if (!isValidPhone) return false - - const isValidLength = phone.split("").filter((c) => !isNaN(Number(c))).length >= PHONE_LENGTH - if (!isValidLength) return false - - return true -} diff --git a/packages/effect-app/src/withToast.ts b/packages/effect-app/src/withToast.ts deleted file mode 100644 index 9b0b6bb73..000000000 --- a/packages/effect-app/src/withToast.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as Cause from "effect/Cause" -import * as Fiber from "effect/Fiber" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" -import type * as Option from "./Option.ts" -import * as S from "./Schema.ts" -import { CurrentToastId, Toast, type ToastId } from "./toast.ts" -import { wrapEffect } from "./utils.ts" - -export interface ToastOptions, WaiR, SucR, ErrR> { - stableToastId?: undefined | string | ((...args: Args) => string | undefined) - timeout?: number - showSpanInfo?: false - groupId?: string - onWaiting: - | string - | ((...args: Args) => string | null) - | null - | (( - ...args: Args - ) => Effect.Effect) - onSuccess: - | string - | ((a: A, ...args: Args) => string | null) - | null - | (( - a: A, - ...args: Args - ) => Effect.Effect) - onFailure: - | string - | (( - error: Option.Option, - ...args: Args - ) => string | { level: "warn" | "error"; message: string }) - | (( - error: Option.Option, - ...args: Args - ) => Effect.Effect) -} - -// @effect-diagnostics-next-line missingEffectServiceDependency:off -export class WithToast extends Context.Service()("WithToast", { - make: Effect.gen(function*() { - const toast = yield* Toast - return ( - options: ToastOptions - ) => - Effect.fnUntraced(function*(self: Effect.Effect, ...args: Args) { - const baseTimeout = options.timeout ?? 3_000 - - const stableToastId = typeof options.stableToastId === "function" - ? options.stableToastId(...args) - : options.stableToastId - - const requestId: string = yield* Effect.currentSpan.pipe( - Effect.map((span) => span.traceId), - Effect.orElseSucceed(() => S.StringId.make()) - ) - const groupId = options.groupId - const meta = { ...(groupId !== undefined ? { groupId } : {}), requestId } - - const t = yield* wrapEffect(options.onWaiting)(...args) - const toastId: ToastId | undefined = t === null - ? stableToastId - : stableToastId ?? `wait-${Math.random().toString(36).slice(2)}` - - const waitingFiber = t === null ? undefined : yield* Effect.forkChild( - Effect.sleep("1 seconds").pipe( - Effect.andThen(toast.info(t, { id: toastId!, timeout: Infinity, ...meta })) - ) - ) - const interruptWaiting = waitingFiber ? Fiber.interrupt(waitingFiber) : Effect.void - - return yield* self.pipe( - Effect.tap(Effect.fnUntraced(function*(a) { - yield* interruptWaiting - const t = yield* wrapEffect(options.onSuccess)(a, ...args) - if (t === null) { - return - } - yield* toast.success( - t, - toastId !== undefined - ? { id: toastId, timeout: baseTimeout, ...meta } - : { timeout: baseTimeout, ...meta } - ) - })), - Effect.tapCause(Effect.fnUntraced(function*(cause) { - yield* interruptWaiting - yield* Effect.logDebug( - "WithToast - caught error cause: " + Cause.squash(cause), - Cause.hasInterruptsOnly(cause), - cause - ) - - if (Cause.hasInterruptsOnly(cause)) { - if (toastId) yield* toast.dismiss(toastId) - return - } - - const spanInfo = options.showSpanInfo !== false - ? yield* Effect.currentSpan.pipe( - Effect.map((span) => `\nTrace: ${span.traceId}\nSpan: ${span.spanId}`), - Effect.orElseSucceed(() => "") - ) - : "" - - const t = yield* wrapEffect(options.onFailure)(Cause.findErrorOption(cause), ...args) - const opts = { timeout: baseTimeout * 2, ...meta } - - if (typeof t === "object") { - const message = t.message + spanInfo - return t.level === "warn" - ? yield* toast.warning(message, toastId !== undefined ? { ...opts, id: toastId } : opts) - : yield* toast.error(message, toastId !== undefined ? { ...opts, id: toastId } : opts) - } - yield* toast.error(t + spanInfo, toastId !== undefined ? { ...opts, id: toastId } : opts) - }, Effect.uninterruptible)), - toastId !== undefined ? Effect.provideService(CurrentToastId, CurrentToastId.of({ toastId })) : (_) => _ - ) - }) - }) -}) { - static readonly DefaultWithoutDependencies = Layer.effect(this, this.make) - static readonly Default = this.DefaultWithoutDependencies - - static readonly handle = , R, WaiR = never, SucR = never, ErrR = never>( - options: ToastOptions - ): (self: Effect.Effect, ...args: Args) => Effect.Effect => - (self, ...args) => this.use((_) => _(options)(self, ...args)) -} diff --git a/packages/effect-app/test/moreStrings.test.ts b/packages/effect-app/test/moreStrings.test.ts deleted file mode 100644 index 88d014338..000000000 --- a/packages/effect-app/test/moreStrings.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as S from "effect-app/Schema" -import * as Arbitrary from "effect/Arbitrary" -import * as Effect from "effect/Effect" -import { customRandom, urlAlphabet } from "nanoid" -import { test } from "vitest" - -const nanoidAlphabet = new Set(urlAlphabet) - -const isNanoId = (value: string) => value.length === 21 && Array.from(value).every((char) => nanoidAlphabet.has(char)) - -test("StringId make generates nanoid-shaped values", () => { - for (let i = 0; i < 20; i++) { - const value = S.StringId.make() - expect(isNanoId(value)).toBe(true) - expect(S.is(S.StringId)(value)).toBe(true) - } -}) - -test("StringId native Arbitrary samples unique nanoid-shaped values", () => { - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(S.StringId), { count: 20, seed: 5 })) - expect(new Set(samples).size).toBe(20) - for (const value of samples) { - expect(isNanoId(value)).toBe(true) - expect(S.is(S.StringId)(value)).toBe(true) - } -}) - -// Pre Effect 4.0.0-rc.114 StringIdArb (rc.112, 8bd5bfe64): -// fc.uint8Array({ minLength: 10 * 21, maxLength: 10 * 21 }) -// .map((bytes) => customRandom(urlAlphabet, 21, (n) => bytes.subarray(0, n))()) -test("StringId toCodecArbitrary uses the pre-rc.114 StringIdArb byte map", () => { - const size = 21 - const length = 10 * size - const stringIdArbMap = (bytes: Uint8Array) => customRandom(urlAlphabet, size, (n) => bytes.subarray(0, n))() - const bytes = Uint8Array.from({ length }, (_, i) => (i * 17 + 3) & 0xff) - const fromOld = stringIdArbMap(bytes) - expect(isNanoId(fromOld)).toBe(true) - // Same mapping the schema's toCodecArbitrary decode runs (customRandom + subarray). - expect(customRandom(urlAlphabet, size, (n) => bytes.subarray(0, n))()).toBe(fromOld) -}) - -test("prefixedStringId native Arbitrary matches pre-rc.114 prefix + nanoid map", () => { - const FooId = S.prefixedStringId()("foo", "FooId") - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(FooId), { count: 10, seed: 5 })) - expect(new Set(samples).size).toBe(10) - for (const value of samples) { - expect(value.startsWith("foo-")).toBe(true) - expect(S.is(FooId)(value)).toBe(true) - const rest = value.slice("foo-".length) - expect(rest.length).toBeGreaterThan(0) - expect(Array.from(rest).every((char) => nanoidAlphabet.has(char))).toBe(true) - } -}) - -test("Url native Arbitrary samples http(s) URLs", () => { - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(S.Url), { count: 10, seed: 5 })) - expect(new Set(samples).size).toBeGreaterThan(1) - for (const value of samples) { - expect(value.startsWith("https://")).toBe(true) - expect(S.is(S.Url)(value)).toBe(true) - } -}) diff --git a/packages/effect-app/test/rpc.test.ts b/packages/effect-app/test/rpc.test.ts deleted file mode 100644 index d76ed2e00..000000000 --- a/packages/effect-app/test/rpc.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type * as Effect from "effect/Effect" -import type * as Option from "effect/Option" -import { expect, test } from "vitest" -import { makeRpcClient, NotLoggedInError, UnauthorizedError } from "../src/client.js" -import { S } from "../src/index.js" -import { RpcContextMap } from "../src/rpc.js" - -export class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError), - requireRoles: RpcContextMap.makeCustom()(UnauthorizedError, Array()), - test: RpcContextMap.make()(S.Never) -}) {} - -const stubMiddleware = { - requestContextMap: RequestContextMap.config, - requestContext: undefined as never -} -const { TaggedRequestFor } = makeRpcClient(stubMiddleware) -const TaggedRequest = TaggedRequestFor("Test").Query - -export class Stats extends TaggedRequest()("Stats", {}, { - allowedRoles: ["manager"], - success: { - usersActive24Hours: S.Finite, - usersActiveLastWeek: S.Finite, - newUsersLast24Hours: S.Finite, - newUsersLastWeek: S.Finite - } -}) {} - -declare const _stats: typeof Stats.Type -declare const _statsSuccess: typeof Stats.success.Type -declare const _statsError: typeof Stats.error.Type -declare const _statsRequestType: typeof Stats.type - -test("Void decodes and encodes as void", () => { - const statsFromMake = Stats.make({}) - const statsFromMakeOption = Stats.makeOption({}) - const statsFromMakeEffect = Stats.makeEffect({}) - - expect(S.decodeUnknownSync(S.Void)(undefined)).toBe(undefined) - expect(S.is(S.Void)(undefined)).toBe(true) - expect(S.decodeUnknownSync(S.Void)("test")).toBe(undefined) - expect(S.is(S.Void)("test")).toBe(true) - expect(S.encodeUnknownSync(S.Void)("test")).toBe(undefined) - expect(S.encodeUnknownSync(S.toCodecJson(S.Void))("test")).toBe(null) - expectTypeOf().toEqualTypeOf() - expectTypeOf().toEqualTypeOf<{ - readonly usersActive24Hours: number - readonly usersActiveLastWeek: number - readonly newUsersLast24Hours: number - readonly newUsersLastWeek: number - }>() - // Resource error carries only `config.error` (and optional `generalErrors`); rcm-derived - // middleware errors no longer leak into `resource.error` — they reach the wire via the - // middleware tag attached to the rpc group (`rpc.middlewares[*].error` failure-union). - expectTypeOf().toEqualTypeOf() - expectTypeOf().toEqualTypeOf<"query">() - expectTypeOf(statsFromMake).toEqualTypeOf() - expectTypeOf(statsFromMakeOption).toEqualTypeOf>() - expectTypeOf(statsFromMakeEffect).toEqualTypeOf>() -}) diff --git a/packages/effect-app/test/rpcMiddlewareContext.test.ts b/packages/effect-app/test/rpcMiddlewareContext.test.ts deleted file mode 100644 index 654a20c7a..000000000 --- a/packages/effect-app/test/rpcMiddlewareContext.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { expectTypeOf, test } from "vitest" -import { makeRpcClient, NotLoggedInError, UnauthorizedError } from "../src/client.js" -import { RpcContextMap } from "../src/rpc.js" - -// A stand-in for an app service (e.g. `UserProfile`) that the `allowAnonymous` -// inverted middleware provides into the handler context whenever auth is -// required (i.e. `allowAnonymous` is not `true`). -interface UserProfile { - readonly _tag: "UserProfile" -} - -// Mirrors a real app request-context map: one inverted key providing a service -// by default (unless opted out), and one custom (role) key. -class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError), - allowRoles: RpcContextMap.makeCustom()(UnauthorizedError, Array()) -}) {} - -type Map = typeof RequestContextMap.config - -// The context a handler receives for a given rpc config — exactly the -// `GetEffectContext<_RC, rpc.config>` the router computes for the handler -// (see `RpcMiddleware.ts`). Testing it against config literals pins down the -// middleware semantics directly. -type CtxFor = RpcContextMap.GetEffectContext - -test("`GetEffectContext`: the inverted `UserProfile` provision is gated by `allowAnonymous`, not `allowRoles`", () => { - // Empty config = nothing opted out → auth required → `UserProfile` provided. - expectTypeOf>().toEqualTypeOf() - - // `allowRoles` does not gate `UserProfile` — still provided. - expectTypeOf>().toEqualTypeOf() - - // Negative case: opting into anonymous removes the `UserProfile` provision. - expectTypeOf>().toBeNever() - - // Anonymous wins over roles for the inverted provision. - expectTypeOf>().toBeNever() - - // Regression documentation: a config that collapses to `Record` - // (what the no-config command overload used to produce) WRONGLY drops - // `UserProfile` — `keyof Record` is `string`, so `allowAnonymous` - // satisfies `key extends keyof T`, and `T["allowAnonymous"]` (`never`) `extends - // true`, taking the anonymous branch. This is exactly why the overload must - // yield `{}` (see the command-builder test below). - expectTypeOf>>().toBeNever() -}) - -// End-to-end guard for the actual fix: a command defined with NO config object -// must still provide `UserProfile` (auth required by default). This exercises the -// no-config `Command` overload, which now yields an empty `{}` config instead of -// `Record`. -const { TaggedRequestFor } = makeRpcClient({ - requestContextMap: RequestContextMap.config, - requestContext: undefined as never -}) -const Command = TaggedRequestFor("Test").Command - -class NoConfigCmd extends Command()("NoConfigCmd", {}) {} - -test("a no-config command still provides the auth-required `UserProfile` service", () => { - expectTypeOf>().toEqualTypeOf() -}) diff --git a/packages/effect-app/test/schema.test.ts b/packages/effect-app/test/schema.test.ts deleted file mode 100644 index bb9a15d32..000000000 --- a/packages/effect-app/test/schema.test.ts +++ /dev/null @@ -1,681 +0,0 @@ -// import { generateFromArbitrary } from "@effect-app/infra/test" -import * as Array from "effect-app/Array" -import * as S from "effect-app/Schema" -import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema" -import * as Effect from "effect/Effect" -import * as Struct from "effect/Struct" -import { describe, expect, expectTypeOf, test } from "vitest" - -const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) }) -test("works", () => { - console.log(S.StringId.make()) - // console.log(generateFromArbitrary(S.A.make(A)).value) - console.log(S.AST.resolveTitle(S.Email.ast)) - console.log(S.AST.resolveDescription(S.Email.ast)) - console.log(S.toJsonSchemaDocument(S.Email)) - console.log(S.toJsonSchemaDocument(S.Email)) - console.log(S.decodeExit(A)({ a: Array.range(1, 256).join(""), email: "hello" })) -}) - -test("literal default works", () => { - const l = S.Literals(["a", "b"]) - expect(l.Default).toBe("a") - expectTypeOf(l.Default).toEqualTypeOf<"a">() - const s = S.Struct({ l: l.withConstructorDefault }) - expect(s.make({}).l).toBe("a") - - const l2 = l.changeDefault("b") - const s2 = S.Struct({ l: l2.withConstructorDefault }) - expect(s2.make({}).l).toBe("b") -}) - -// Toy schema for `dropConstructorDefault`: a field with a constructor -// default that should NOT auto-populate when deriving a partial-update -// schema from a "create" schema. -const Person = S.Struct({ - name: S.String, - notes: S.NullOr(S.String).pipe(S.optionalKey, S.withConstructorDefault(Effect.succeed(null))) -}) - -test("plain Struct.omit leaks the inherited constructor default (the bug)", () => { - const UpdatePersonBuggy = S.Struct(Struct.omit(Person.fields, [] as const)) - expect(UpdatePersonBuggy.make({ name: "Mario" })).toEqual({ name: "Mario", notes: null }) -}) - -test("Struct.map(S.dropConstructorDefault) strips the inherited constructor default (the fix)", () => { - const UpdatePersonFixed = S.Struct(Struct.map(Struct.omit(Person.fields, [] as const), S.dropConstructorDefault)) - expect(UpdatePersonFixed.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("Struct.map(S.dropConstructorDefault) still omits the requested keys, like Struct.omit", () => { - const NameOnly = S.Struct(Struct.map(Struct.omit(Person.fields, ["notes"] as const), S.dropConstructorDefault)) - expect(NameOnly.make({ name: "Mario" })).toEqual({ name: "Mario" }) - expectTypeOf().toEqualTypeOf<{ readonly name: string }>() -}) - -test("makeExactOptional drops the inherited constructor default too", () => { - const UpdatePerson = S.Struct(S.makeExactOptional(Person.fields)) - expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("dropConstructorDefault stays 'no-default' after annotate/annotateKey (Rebuild fix)", () => { - const notesDropped = S.dropConstructorDefault(Person.fields.notes) - expectTypeOf<(typeof notesDropped)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const notesAnnotated = notesDropped.annotate({ description: "no longer defaulted" }) - expectTypeOf<(typeof notesAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const notesKeyAnnotated = notesDropped.annotateKey({ description: "no longer defaulted (key)" }) - expectTypeOf<(typeof notesKeyAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const UpdatePerson = S.Struct({ name: Person.fields.name, notes: notesAnnotated }) - expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("NonEmptyString255.Type uses the named brand alias", () => { - type A = typeof S.NonEmptyString255.Type - type B = string & S.NonEmptyString255Brand - expectTypeOf().toEqualTypeOf() -}) - -test("Opaque accepts an explicit Encoded type", () => { - interface User { - readonly id: string - readonly _tag: "User" - } - - interface UserEncoded { - readonly id: string - } - - const baseSchema = S.Struct({ id: S.String, name: S.String }) - const _UserSchema = S.Opaque()(baseSchema) - - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().not.toEqualTypeOf>() -}) - -test("Opaque with one generic keeps the base encoded shape", () => { - interface User { - readonly id: string - } - - const baseSchema = S.Struct({ id: S.String }) - const _UserSchema = S.Opaque()(baseSchema) - - expectTypeOf>().toEqualTypeOf<{ readonly id: string }>() - expectTypeOf>().toEqualTypeOf() -}) - -test("Opaque preserves optional Struct.make input", () => { - interface User { - readonly a?: string | undefined - readonly b?: number | undefined - } - - const schema = S.Opaque()(S.Struct({ - a: S.optional(S.String), - b: S.optional(S.Number) - })) - - const made = schema.make() - expect(made).toEqual({}) - expectTypeOf(made).toEqualTypeOf() -}) - -test("Opaque preserves optional TaggedStruct.make input", () => { - interface OnlyTag { - readonly _tag: "OnlyTag" - } - - const schema = S.Opaque()(S.TaggedStruct("OnlyTag", {})) - - const made = schema.make() - expect(made).toEqual({ _tag: "OnlyTag" }) - expectTypeOf(made).toEqualTypeOf() -}) - -test("S.Literals([\"A\", \"B\"]).Default is typed as \"A\"", () => { - const l = S.Literals(["A", "B"]) - expect(l.Default).toBe("A") - expectTypeOf(l.Default).toEqualTypeOf<"A">() -}) - -test("Struct.make accepts void when all fields are optional", () => { - const schema = S.Struct({ - a: S.optional(S.String), - b: S.optional(S.Number) - }) - - const made = schema.make() - expect(made).toEqual({}) - expectTypeOf(made).toEqualTypeOf<{ readonly a?: string | undefined; readonly b?: number | undefined }>() -}) - -test("StructNestedEncoded resolves Struct and from.Encoded", () => { - const plain = S.Struct({ a: S.String, b: S.NullOr(S.String) }) - const encodedKeys = plain.pipe(S.encodeKeys({ b: "b_encoded" })) - - expectTypeOf>().toEqualTypeOf<{ - readonly a: string - readonly b: string | null - }>() - - expectTypeOf>().toEqualTypeOf<{ - readonly a: string - readonly b_encoded: string | null - }>() -}) - -test("TaggedStruct.make accepts void when only constructor-default fields exist", () => { - const schema = S.TaggedStruct("OnlyTag", {}) - - const made = schema.make() - expect(made).toEqual({ _tag: "OnlyTag" }) - expectTypeOf(made).toEqualTypeOf<{ readonly _tag: "OnlyTag" }>() -}) - -test("tagged union derives tag map and tags from v4 literal ast", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }), - S.TaggedStruct("C", { c: S.Boolean }) - ]) - const caseA = schema.cases["A"] - const caseB = schema.cases["B"] - const caseC = schema.cases["C"] - const isAOrB = schema.isAnyOf(["A", "B"]) - - expect(caseA.fields._tag.ast.literal).toBe("A") - expect(caseB.fields._tag.ast.literal).toBe("B") - expect(caseC.fields._tag.ast.literal).toBe("C") - expect(S.decodeSync(schema.tags)("A")).toBe("A") - expect(S.decodeSync(schema.tags)("B")).toBe("B") - expect(S.decodeSync(schema.tags)("C")).toBe("C") - expect(() => S.decodeUnknownSync(schema.tags)("D")).toThrow() - - expect(schema.guards.A({ _tag: "A", a: "ok" })).toBe(true) - expect(schema.guards.A({ _tag: "B", b: 1 })).toBe(false) - expect(schema.guards.B({ _tag: "B", b: 1 })).toBe(true) - expect(schema.guards.B({ _tag: "A", a: "ok" })).toBe(false) - expect(schema.guards.C({ _tag: "C", c: true })).toBe(true) - expect(schema.guards.C({ _tag: "A", a: "ok" })).toBe(false) - - expect(isAOrB({ _tag: "A", a: "ok" })).toBe(true) - expect(isAOrB({ _tag: "B", b: 1 })).toBe(true) - expect(isAOrB({ _tag: "C", c: true })).toBe(false) -}) - -test("TaggedUnion tags returns a Literals schema with correct literal values", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("X", { x: S.String }), - S.TaggedStruct("Y", { y: S.Finite }) - ]) - - expect(schema.tags.literals).toEqual(["X", "Y"]) - expectTypeOf(schema.tags.literals).toMatchTypeOf() -}) - -test("TaggedUnion tags.pick returns a subset of the tag literals", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }), - S.TaggedStruct("C", { c: S.Boolean }) - ]) - - const subset = schema.tags.pick(["A", "C"]) - expect(subset.literals).toEqual(["A", "C"]) - expect(S.decodeSync(subset)("A")).toBe("A") - expect(S.decodeSync(subset)("C")).toBe("C") - expect(() => S.decodeUnknownSync(subset)("B")).toThrow() -}) - -test("tags standalone function extracts tags from member schemas", () => { - const members = [ - S.TaggedStruct("Foo", { foo: S.String }), - S.TaggedStruct("Bar", { bar: S.Finite }) - ] as const - - const tagSchema = S.tags(members) - expect(tagSchema.literals).toEqual(["Foo", "Bar"]) - expect(S.decodeSync(tagSchema)("Foo")).toBe("Foo") - expect(S.decodeSync(tagSchema)("Bar")).toBe("Bar") - expect(() => S.decodeUnknownSync(tagSchema)("Baz")).toThrow() -}) - -test("ExtendTaggedUnion adds tags to an existing Union", () => { - const union = S.Union([ - S.TaggedStruct("P", { p: S.String }), - S.TaggedStruct("Q", { q: S.Finite }) - ]) - const extended = S.ExtendTaggedUnion(union) - - expect(extended.tags.literals).toEqual(["P", "Q"]) - expect(S.decodeSync(extended.tags)("P")).toBe("P") - expect(S.decodeSync(extended.tags)("Q")).toBe("Q") - expect(() => S.decodeUnknownSync(extended.tags)("R")).toThrow() - expect(extended.cases["P"].fields._tag.ast.literal).toBe("P") - expect(extended.guards.P({ _tag: "P", p: "ok" })).toBe(true) - expect(extended.guards.P({ _tag: "Q", q: 1 })).toBe(false) -}) - -test("TaggedUnion match dispatches on _tag", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const matcher = schema.match({ - A: (v) => `got A: ${v.a}`, - B: (v) => `got B: ${v.b}` - }) - expect(matcher({ _tag: "A", a: "hello" })).toBe("got A: hello") - expect(matcher({ _tag: "B", b: 42 })).toBe("got B: 42") -}) - -test("TaggedUnion with single member", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("Only", { val: S.String }) - ]) - - expect(schema.tags.literals).toEqual(["Only"]) - expect(S.decodeSync(schema.tags)("Only")).toBe("Only") - expect(() => S.decodeUnknownSync(schema.tags)("Other")).toThrow() - expect(schema.guards.Only({ _tag: "Only", val: "x" })).toBe(true) -}) - -test("TaggedUnion tags type is narrowed to the exact tag literals", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("Alpha", { a: S.String }), - S.TaggedStruct("Beta", { b: S.Finite }), - S.TaggedStruct("Gamma", { c: S.Boolean }) - ]) - - type Tags = S.Schema.Type - expectTypeOf().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">() -}) - -test("TaggedUnion with encodeKeys renaming a non-tag key", () => { - const MemberA = S.TaggedStruct("A", { firstName: S.String }).pipe( - S.encodeKeys({ firstName: "first_name" }) - ) - const MemberB = S.TaggedStruct("B", { lastName: S.Finite }).pipe( - S.encodeKeys({ lastName: "last_name" }) - ) - - const schema = S.TaggedUnion([MemberA, MemberB]) - - expect(schema.tags.literals).toEqual(["A", "B"]) - expect(S.decodeSync(schema.tags)("A")).toBe("A") - expect(S.decodeSync(schema.tags)("B")).toBe("B") - - // decode from encoded (snake_case) to decoded (camelCase) - const decoded = S.decodeUnknownSync(schema)({ _tag: "A", first_name: "Alice" }) - expect(decoded).toEqual({ _tag: "A", firstName: "Alice" }) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", last_name: 42 }) - expect(decoded2).toEqual({ _tag: "B", lastName: 42 }) - - // encode back to snake_case - const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" }) - expect(encoded).toEqual({ _tag: "A", first_name: "Alice" }) - - // guards work on decoded values - expect(schema.guards.A({ _tag: "A", firstName: "Alice" })).toBe(true) - expect(schema.guards.A({ _tag: "B", lastName: 42 })).toBe(false) - expect(schema.guards.B({ _tag: "B", lastName: 42 })).toBe(true) -}) - -test("TaggedUnion with TaggedClass members", () => { - class Foo extends S.TaggedClass()("Foo", { name: S.String }) {} - class Bar extends S.TaggedClass()("Bar", { count: S.Finite }) {} - - const schema = S.TaggedUnion([Foo, Bar]) - - expect(schema.tags.literals).toEqual(["Foo", "Bar"]) - expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo") - expect(S.decodeSync(schema.tags)("Bar")).toBe("Bar") - expect(() => S.decodeUnknownSync(schema.tags)("Baz")).toThrow() - - const decoded = S.decodeUnknownSync(schema)({ _tag: "Foo", name: "Alice" }) - expect(decoded).toBeInstanceOf(Foo) - expect(decoded).toEqual(new Foo({ name: "Alice" })) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "Bar", count: 3 }) - expect(decoded2).toBeInstanceOf(Bar) - expect(decoded2).toEqual(new Bar({ count: 3 })) - - expect(schema.guards.Foo(new Foo({ name: "Alice" }))).toBe(true) - expect(schema.guards.Foo(new Bar({ count: 3 }))).toBe(false) - expect(schema.guards.Bar(new Bar({ count: 3 }))).toBe(true) -}) - -describe("ReadonlySetFromArray", () => { - test("decodes an array of strings to a Set", () => { - const schema = S.ReadonlySetFromArray(S.String) - const decoded = S.decodeUnknownSync(schema)(["a", "b", "c"]) - expect(decoded).toEqual(new Set(["a", "b", "c"])) - }) - - test("encodes a Set back to an array", () => { - const schema = S.ReadonlySetFromArray(S.String) - const encoded = S.encodeSync(schema)(new Set(["a", "b"])) - expect(encoded).toEqual(["a", "b"]) - }) - - test("decodes with NumberFromString as value", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - const decoded = S.decodeUnknownSync(schema)(["1", "2", "3"]) - expect(decoded).toEqual(new Set([1, 2, 3])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as value", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - const encoded = S.encodeSync(schema)(new Set([1, 2, 3])) - expect(encoded).toEqual(["1", "2", "3"]) - }) - - test("rejects invalid input", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - expect(() => S.decodeUnknownSync(schema)([1, 2])).toThrow() - }) -}) - -describe("ReadonlyMapFromArray", () => { - test("decodes an array of tuples to a Map", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite }) - const decoded = S.decodeUnknownSync(schema)([["a", 1], ["b", 2]]) - expect(decoded).toEqual(new Map([["a", 1], ["b", 2]])) - }) - - test("encodes a Map back to an array of tuples", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite }) - const encoded = S.encodeSync(schema)(new Map([["a", 1], ["b", 2]])) - expect(encoded).toEqual([["a", 1], ["b", 2]]) - }) - - test("decodes with NumberFromString as key", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - const decoded = S.decodeUnknownSync(schema)([["1", "one"], ["2", "two"]]) - expect(decoded).toEqual(new Map([[1, "one"], [2, "two"]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as key", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - const encoded = S.encodeSync(schema)(new Map([[1, "one"], [2, "two"]])) - expect(encoded).toEqual([["1", "one"], ["2", "two"]]) - }) - - test("decodes with NumberFromString as value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.NumberFromString }) - const decoded = S.decodeUnknownSync(schema)([["a", "10"], ["b", "20"]]) - expect(decoded).toEqual(new Map([["a", 10], ["b", 20]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.NumberFromString }) - const encoded = S.encodeSync(schema)(new Map([["a", 10], ["b", 20]])) - expect(encoded).toEqual([["a", "10"], ["b", "20"]]) - }) - - test("decodes with NumberFromString as both key and value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.NumberFromString }) - const decoded = S.decodeUnknownSync(schema)([["1", "10"], ["2", "20"]]) - expect(decoded).toEqual(new Map([[1, 10], [2, 20]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("rejects invalid input", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - expect(() => S.decodeUnknownSync(schema)([[1, "val"]])).toThrow() - }) -}) - -describe("ReadonlySet (with withConstructorDefault)", () => { - test("make provides withConstructorDefault", () => { - const schema = S.ReadonlySet(S.NumberFromString) - const struct = S.Struct({ items: schema.withConstructorDefault }) - const made = struct.make({}) - expect(made.items).toEqual(new Set()) - }) - - test("decodes a Set with NumberFromString values", () => { - const schema = S.ReadonlySet(S.NumberFromString) - const decoded = S.decodeUnknownSync(schema)(new Set(["1", "2"])) - expect(decoded).toEqual(new Set([1, 2])) - }) - - test("Encoded is a Set, not an array", () => { - const schema = S.ReadonlySet(S.String) - const encoded = S.encodeSync(schema)(new Set(["a"])) - expect(encoded).toEqual(new Set(["a"])) - expectTypeOf(encoded).toEqualTypeOf>() - }) -}) - -describe("ReadonlyMap (with withConstructorDefault)", () => { - test("make provides withConstructorDefault", () => { - const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String }) - const struct = S.Struct({ items: schema.withConstructorDefault }) - const made = struct.make({}) - expect(made.items).toEqual(new Map()) - }) - - test("decodes a Map with NumberFromString keys", () => { - const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String }) - const decoded = S.decodeUnknownSync(schema)(new Map([["1", "one"]])) - expect(decoded).toEqual(new Map([[1, "one"]])) - }) - - test("Encoded is a Map, not an array of tuples", () => { - const schema = S.ReadonlyMap({ key: S.String, value: S.Finite }) - const encoded = S.encodeSync(schema)(new Map([["a", 1]])) - expect(encoded).toEqual(new Map([["a", 1]])) - expectTypeOf(encoded).toEqualTypeOf>() - }) -}) - -describe("JSON Schema", () => { - test("Email has format, minLength, maxLength", () => { - const doc = S.toJsonSchemaDocument(S.Email) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Email" }, - definitions: { - Email: { - type: "string", - title: "Email", - description: "an email according to RFC 5322", - format: "email", - minLength: 2, - maxLength: 998 - } - } - }) - }) - - test("Email specialJsonSchemaDocument flattens allOf", () => { - const doc = specialJsonSchemaDocument(S.Email) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Email" }, - definitions: { - Email: { - type: "string", - title: "Email", - description: "an email according to RFC 5322", - format: "email", - minLength: 2, - maxLength: 998 - } - } - }) - }) - - test("Date Encoded is Date; JSON codec encodes ISO strings", () => { - const d = new Date("2024-01-01T00:00:00.000Z") - expect(S.decodeUnknownSync(S.Date)(d)).toBe(d) - expect(S.encodeSync(S.Date)(d)).toBe(d) - expect(S.encodeSync(S.toCodecJson(S.Date))(d)).toBe("2024-01-01T00:00:00.000Z") - const doc = S.toJsonSchemaDocument(S.Date) - expect(doc.dialect).toBe("draft-2020-12") - expect(doc.schema).toEqual({ type: "string" }) - }) - - test("DateFromString keeps string Encoded", () => { - const doc = S.toJsonSchemaDocument(S.DateFromString) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/DateOrInvalid" }, - definitions: { - DateOrInvalid: { - type: "string", - description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)", - format: "date-time" - } - } - }) - }) - - test("PhoneNumber has format phone", () => { - const doc = specialJsonSchemaDocument(S.PhoneNumber) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/PhoneNumber" }, - definitions: { - PhoneNumber: { - type: "string", - title: "PhoneNumber", - description: "a phone number with at least 7 digits", - format: "phone" - } - } - }) - }) - - test("Url has format uri", () => { - const doc = specialJsonSchemaDocument(S.Url) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Url" }, - definitions: { - Url: { - type: "string", - title: "Url", - format: "uri" - } - } - }) - }) -}) - -describe("generateGuards", () => { - const StateSchema = S.TaggedUnion([ - S.TaggedStruct("Active", { since: S.String }), - S.TaggedStruct("Inactive", { reason: S.String }), - S.TaggedStruct("Pending", { eta: S.Finite }) - ]) - - type State = S.Schema.Type - type Entity = { readonly state: State; readonly name: string } - - const { isActive, isAnyOf, isInactive, isPending } = StateSchema.generateGuards("state") - - test("isActive narrows to Active member", () => { - const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - expect(isActive(entity)).toBe(true) - if (isActive(entity)) { - expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>() - } - }) - - test("isActive returns false for non-Active", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isActive(entity)).toBe(false) - }) - - test("isInactive narrows to Inactive member", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isInactive(entity)).toBe(true) - }) - - test("isPending narrows to Pending member", () => { - const entity: Entity = { state: { _tag: "Pending", eta: 42 }, name: "foo" } - expect(isPending(entity)).toBe(true) - }) - - test("isAnyOf narrows to union of specified members", () => { - const isActiveOrPending = isAnyOf(["Active", "Pending"]) - const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - const pending: Entity = { state: { _tag: "Pending", eta: 5 }, name: "bar" } - const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" } - - expect(isActiveOrPending(active)).toBe(true) - expect(isActiveOrPending(pending)).toBe(true) - expect(isActiveOrPending(inactive)).toBe(false) - - if (isActiveOrPending(active)) { - expectTypeOf(active.state).toEqualTypeOf< - { readonly _tag: "Active"; readonly since: string } | { readonly _tag: "Pending"; readonly eta: number } - >() - } - }) - - test("guards use schema-based validation (built-in guards)", () => { - expect(StateSchema.guards.Active({ _tag: "Active" })).toBe(false) - expect(StateSchema.guards.Active({ _tag: "Active", since: "2024-01-01" })).toBe(true) - }) -}) - -describe("generateGuardsFor", () => { - const StateSchema = S.TaggedUnion([ - S.TaggedStruct("Active", { since: S.String }), - S.TaggedStruct("Inactive", { reason: S.String }), - S.TaggedStruct("Pending", { eta: S.Finite }) - ]) - - type State = S.Schema.Type - type Entity = { readonly state: State; readonly name: string } - - const { isActive, isAnyOf } = StateSchema.generateGuardsFor()("state") - - test("isActive narrows to Active member", () => { - const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - expect(isActive(entity)).toBe(true) - if (isActive(entity)) { - expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>() - } - }) - - test("isActive returns false for non-Active", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isActive(entity)).toBe(false) - }) - - test("isAnyOf narrows to union of specified members", () => { - const isActiveOrPending = isAnyOf(["Active", "Pending"]) - const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" } - - expect(isActiveOrPending(active)).toBe(true) - expect(isActiveOrPending(inactive)).toBe(false) - }) - - test("ExtendTaggedUnion also exposes generateGuardsFor", () => { - const union = S.Union([ - S.TaggedStruct("X", { x: S.String }), - S.TaggedStruct("Y", { y: S.Finite }) - ]) - const extended = S.ExtendTaggedUnion(union) - type Obj = { readonly field: S.Schema.Type } - const { isX, isY } = extended.generateGuardsFor()("field") - - expect(isX({ field: { _tag: "X", x: "hi" } })).toBe(true) - expect(isX({ field: { _tag: "Y", y: 1 } })).toBe(false) - expect(isY({ field: { _tag: "Y", y: 1 } })).toBe(true) - }) -}) diff --git a/packages/effect-app/test/secretURL.test.ts b/packages/effect-app/test/secretURL.test.ts deleted file mode 100644 index af2ab186c..000000000 --- a/packages/effect-app/test/secretURL.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -import * as Chunk from "effect/Chunk" -import * as Config from "effect/Config" -import * as ConfigProvider from "effect/ConfigProvider" -import * as Effect from "effect/Effect" -import { describe, expect, test } from "vitest" -import { fromChunk, fromString, isSecretURL, make, secretURL, unsafeWipe, value } from "../src/Config/SecretURL.js" - -const testUrls = [ - "https://example.com/path?key=secret123", - "http://user:password@host.com:8080/resource", - "postgres://admin:p4ssw0rd@db.internal:5432/mydb", - "redis://default:token@cache.example.com:6379", - "mongodb+srv://user:pass@cluster.mongodb.net/db", - "https://api.example.com/v2/endpoint?token=abc&format=json" -] - -describe("SecretURL", () => { - describe("fromString / value roundtrip", () => { - test.each(testUrls)("preserves %s", (url) => { - const secret = fromString(url) - expect(value(secret)).toBe(url) - }) - }) - - describe("make / value roundtrip", () => { - test.each(testUrls)("preserves %s via byte array", (url) => { - const bytes = url.split("").map((c) => c.charCodeAt(0)) - const secret = make(bytes) - expect(value(secret)).toBe(url) - }) - }) - - describe("fromChunk / value roundtrip", () => { - test.each(testUrls)("preserves %s via Chunk", (url) => { - const chunk = Chunk.fromIterable(url.split("")) - const secret = fromChunk(chunk) - expect(value(secret)).toBe(url) - }) - }) - - describe("toString", () => { - test("redacts the URL, showing only protocol", () => { - const secret = fromString("https://example.com/secret") - expect(String(secret)).toBe("SecretURL(https://)") - }) - - test("shows http protocol", () => { - const secret = fromString("http://example.com") - expect(String(secret)).toBe("SecretURL(http://)") - }) - - test("shows unknown for non-URL strings", () => { - const secret = fromString("not-a-url") - expect(String(secret)).toBe("SecretURL(unknown://)") - }) - }) - - describe("toJSON", () => { - test("returns tag and protocol only", () => { - const secret = fromString("https://example.com/secret") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "https" }) - }) - - test("returns unknown protocol for non-URL", () => { - const secret = fromString("not-a-url") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "unknown" }) - }) - }) - - describe("isSecretURL", () => { - test("returns true for SecretURL", () => { - const secret = fromString("https://example.com") - expect(isSecretURL(secret)).toBe(true) - }) - - test("returns false for plain string", () => { - expect(isSecretURL("https://example.com")).toBe(false) - }) - - test("returns false for plain object", () => { - expect(isSecretURL({ raw: [1, 2, 3] })).toBe(false) - }) - }) - - describe("unsafeWipe", () => { - test("zeroes out raw bytes", () => { - const secret = fromString("https://example.com") - unsafeWipe(secret) - expect(value(secret)).toBe("\0".repeat("https://example.com".length)) - }) - }) - - describe("non-URL strings", () => { - const nonUrls = [ - "just-a-secret-token", - "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.abc", - "sk-1234567890abcdef", - "some random string with spaces and symbols!@#$%" - ] - - test.each(nonUrls)("preserves non-URL value: %s", (input) => { - const secret = fromString(input) - expect(value(secret)).toBe(input) - expect(String(secret)).toBe("SecretURL(unknown://)") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "unknown" }) - }) - }) - - describe("special characters", () => { - test("preserves URLs with encoded characters", () => { - const url = "https://example.com/path?q=hello%20world&name=%C3%A9" - expect(value(fromString(url))).toBe(url) - }) - - test("preserves URLs with unicode", () => { - const url = "https://example.com/café" - expect(value(fromString(url))).toBe(url) - }) - - test("preserves URLs with special query params", () => { - const url = "https://example.com?a=1&b=2&c=foo+bar" - expect(value(fromString(url))).toBe(url) - }) - }) - - describe("secretURL config", () => { - const run = (config: Config.Config, provider: ConfigProvider.ConfigProvider) => - Effect.runSync(config.parse(provider)) - - test("reads from env-style ConfigProvider (QUEUE_URL)", () => { - const url = "https://sqs.us-east-1.amazonaws.com/123456789/my-queue" - const provider = ConfigProvider.fromEnv({ env: { QUEUE_URL: url } }) - const result = run(secretURL("QUEUE_URL"), provider) - expect(value(result)).toBe(url) - expect(String(result)).toBe("SecretURL(https://)") - }) - - test("reads from fromUnknown with nested config ({ queue: { url } })", () => { - const url = "redis://default:token@cache.example.com:6379" - const provider = ConfigProvider.fromUnknown({ queue: { url } }) - const config = secretURL("url").pipe(Config.nested("queue")) - const result = run(config, provider) - expect(value(result)).toBe(url) - expect(String(result)).toBe("SecretURL(redis://)") - }) - - test("reads non-URL secret from env", () => { - const token = "sk-1234567890abcdef" - const provider = ConfigProvider.fromEnv({ env: { API_KEY: token } }) - const result = run(secretURL("API_KEY"), provider) - expect(value(result)).toBe(token) - expect(String(result)).toBe("SecretURL(unknown://)") - }) - - test("rejects empty string", () => { - const provider = ConfigProvider.fromEnv({ env: { QUEUE_URL: "" } }) - expect(() => run(secretURL("QUEUE_URL"), provider)).toThrow() - }) - }) -}) diff --git a/packages/effect-app/test/special.test.ts b/packages/effect-app/test/special.test.ts deleted file mode 100644 index 9931c7757..000000000 --- a/packages/effect-app/test/special.test.ts +++ /dev/null @@ -1,1261 +0,0 @@ -import { InvalidStateError, LoginError, NotFoundError, NotLoggedInError, OptimisticConcurrencyException, ServiceUnavailableError, UnauthorizedError, ValidationError } from "effect-app/client/errors" -import { RequestContext } from "effect-app/RequestContext" -import * as AppSchema from "effect-app/Schema" -import { Class } from "effect-app/Schema/Class" -import { flattenNestedAnyOf, flattenSimpleAllOf, specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema" -import { deduplicateOpenApiSchemas } from "effect-app/Schema/SpecialOpenApi" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as S from "effect/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import { describe, expect, it } from "vitest" - -class _FacadeModel extends AppSchema.Class<_FacadeModel>("FacadeModel")({ value: S.String }) { - get upperValue() { - return this.value.toUpperCase() - } - - static describe(self: { readonly value: string }) { - return `value:${self.value}` - } -} - -interface FacadeModelEncoded { - readonly value: string -} - -interface FacadeModelMake { - readonly value: string -} - -class FacadeModel extends AppSchema.OpaqueFacade< - FacadeModel, - FacadeModelEncoded, - FacadeModelMake, - never, - never ->()(_FacadeModel) { - declare readonly value: string - declare readonly upperValue: string - - get decoratedValue() { - return `facade:${this.value}` - } - - label() { - return this.decoratedValue - } -} - -class _RenamedFacadeModel extends AppSchema.Class<_RenamedFacadeModel>("RenamedFacadeModel")({ - internalName: S.String -}) { - get upperName() { - return this.internalName.toUpperCase() - } -} - -interface RenamedFacadeModelEncoded { - readonly external_name: string -} - -interface RenamedFacadeModelMake { - readonly internalName: string -} - -class RenamedFacadeModel extends AppSchema.OpaqueFacade< - RenamedFacadeModel, - RenamedFacadeModelEncoded, - RenamedFacadeModelMake, - never, - never ->()(_RenamedFacadeModel.pipe(S.encodeKeys({ internalName: "external_name" }))) { - declare readonly internalName: string - declare readonly upperName: string - - get decoratedName() { - return `facade:${this.internalName}` - } -} - -const _StructFacadeModel = AppSchema.Struct({ value: S.String }) - -interface StructFacadeModelEncoded { - readonly value: string -} - -interface StructFacadeModelMake { - readonly value: string -} - -class StructFacadeModel extends AppSchema.OpaqueFacade< - StructFacadeModel, - StructFacadeModelEncoded, - StructFacadeModelMake, - never, - never ->()(_StructFacadeModel) { - declare readonly value: string -} - -const _RenamedStructFacadeModel = AppSchema.Struct({ internalName: S.String }).pipe( - S.encodeKeys({ internalName: "external_name" }) -) - -interface RenamedStructFacadeModelEncoded { - readonly external_name: string -} - -interface RenamedStructFacadeModelMake { - readonly internalName: string -} - -class RenamedStructFacadeModel extends AppSchema.OpaqueFacade< - RenamedStructFacadeModel, - RenamedStructFacadeModelEncoded, - RenamedStructFacadeModelMake, - never, - never ->()(_RenamedStructFacadeModel) { - declare readonly internalName: string -} - -const _RenamedOpaqueStructFacadeModel = AppSchema - .Opaque< - { readonly internalName: string }, - RenamedStructFacadeModelEncoded - >()(AppSchema.Struct({ internalName: S.String })) - .pipe(S.encodeKeys({ internalName: "external_name" })) - -class RenamedOpaqueStructFacadeModel extends AppSchema.OpaqueFacade< - RenamedOpaqueStructFacadeModel, - RenamedStructFacadeModelEncoded, - RenamedStructFacadeModelMake, - never, - never ->()(_RenamedOpaqueStructFacadeModel) { - declare readonly internalName: string -} - -describe("Class", () => { - describe("strict", () => { - it("encoding doesnt accepts plain objects matching the struct (Fields argument)", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("encoding rejects plain objects matching the struct (Struct argument)", () => { - class A extends Class("A")(S.Struct({ a: S.String })) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("decoding still works normally", () => { - class A extends Class("A")({ a: S.String }) {} - - const decoded = S.decodeUnknownSync(A)({ a: "hello" }) - expect(decoded).toBeInstanceOf(A) - expect(decoded.a).toBe("hello") - - expect(() => S.decodeUnknownSync(A)(null)).toThrow() - expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow() - }) - - it("S.is accepts class instances and not matching plain objects", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(S.is(A)(new A({ a: "hello" }))).toBe(true) - expect(S.is(A)({ a: "world" })).toBe(false) - expect(S.is(A)({ a: 1 })).toBe(false) - expect(S.is(A)(null)).toBe(false) - }) - - it("rejects values that don't match the struct", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - expect(() => S.encodeUnknownSync(A)("not an object")).toThrow() - }) - - it("returns a class constructor — new and instanceof work", () => { - class A extends Class("A")({ a: S.String }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - }) - - it("preserves fields and identifier", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - expect(A.identifier).toBe("A") - expect(Object.keys(A.fields)).toStrictEqual(["a", "b"]) - }) - }) - - describe("non strict", () => { - it("encoding accepts plain objects matching the struct (Fields argument)", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("encoding accepts plain objects matching the struct (Struct argument)", () => { - class A extends Class("A")(S.Struct({ a: S.String }), undefined, { strict: false }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("S.is accepts class instances and matching plain objects", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - expect(S.is(A)(new A({ a: "hello" }))).toBe(true) - expect(S.is(A)({ a: "world" })).toBe(true) - expect(S.is(A)({ a: 1 })).toBe(false) - expect(S.is(A)(null)).toBe(false) - }) - }) -}) - -describe("Class constructor", () => { - describe("strict", () => { - it("works as a base class — new, instanceof, encoding plain objects", () => { - class A extends Class("A")({ a: S.String }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - - expect(S.encodeUnknownSync(A)(instance)).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - }) - - it("decoding works normally", () => { - class A extends Class("A")({ a: S.String }) {} - - const decoded = S.decodeUnknownSync(A)({ a: "hello" }) - expect(decoded).toBeInstanceOf(A) - expect(decoded.a).toBe("hello") - - expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow() - }) - - it("exposes fields, identifier", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - expect(A.identifier).toBe("A") - expect(Object.keys(A.fields)).toStrictEqual(["a", "b"]) - }) - }) - - describe("non strict", () => { - it("works as a base class — new, instanceof, encoding plain objects", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - - expect(S.encodeUnknownSync(A)(instance)).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - }) - }) -}) - -describe("TaggedStruct constructor", () => { - it("works with _tag, make, and encoding plain objects", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - expect(instance).toEqual({ _tag: "Circle", radius: 5 }) - - expect(S.encodeUnknownSync(Circle)(instance)).toStrictEqual({ _tag: "Circle", radius: 5 }) - expect(S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 10 })).toStrictEqual({ _tag: "Circle", radius: 10 }) - - expect(() => S.encodeUnknownSync(Circle)(null)).toThrow() - expect(() => S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: "nope" })).toThrow() - }) - - it("decoding works normally", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const decoded = S.decodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 }) - expect(decoded).toEqual({ _tag: "Circle", radius: 5 }) - }) - - it("S.decodeSync(S.toType(X)) should report n length schema error", () => { - class X extends S.Opaque()(S.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - try { - S.decodeSync(S.toType(X))({ _tag: "X", n: "a" /* not length 3 */ }) - expect.fail("expected decode to fail with a SchemaError") - } catch (error) { - expect(S.isSchemaError(error)).toBe(true) - if (S.isSchemaError(error)) { - expect(error.message).toContain("n") - expect(error.message.toLowerCase()).toContain("length") - } - } - }) - - it("exposes fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - expect(Object.keys(Circle.fields)).toContain("_tag") - expect(Object.keys(Circle.fields)).toContain("radius") - }) -}) - -describe("strict declaration option", () => { - it("Class strict: true keeps class-level expected errors", () => { - class X extends Class("X")({ n: S.String.pipe(S.check(S.isMinLength(3))) }, undefined, { strict: true }) {} - - expect(() => S.decodeSync(S.toType(X))({ n: "a" })).toThrow("Expected X") - }) - - it("TaggedStruct preserves field-level expected errors", () => { - class X extends S.Opaque()(AppSchema.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - expect(() => S.decodeSync(S.toType(X))({ _tag: "X", n: "a" })).toThrow() - }) - - it("Class with encoded override strict: true keeps class-level expected errors", () => { - class X extends Class("X")({ n: S.String.pipe(S.check(S.isMinLength(3))) }, undefined, { - strict: true - }) {} - - expect(() => S.decodeSync(S.toType(X))({ n: "a" })).toThrow("Expected X") - }) - - it("TaggedStruct with opaque wrapper decodes tagged input", () => { - class X extends S.Opaque()(AppSchema.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - expect(() => S.decodeSync(S.toType(X))({ _tag: "X", n: "a" })).toThrow() - }) -}) - -describe("Class.copy", () => { - it("creates a new instance with updated fields", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy(instance, { b: 2 }) - expect(copied).toBeInstanceOf(A) - expect(copied.a).toBe("hello") - expect(copied.b).toBe(2) - }) - - it("accepts a function for updates", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy(instance, (a) => ({ b: a.b + 1 })) - expect(copied).toBeInstanceOf(A) - expect(copied.b).toBe(2) - }) - - it("is pipeable", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy({ b: 2 })(instance) - expect(copied).toBeInstanceOf(A) - expect(copied.b).toBe(2) - }) -}) - -describe("TaggedStruct (opaque).copy", () => { - it("creates a new instance with updated fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, { radius: 10 }) - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - }) - - it("accepts a function for updates", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, (c) => ({ radius: c.radius * 2 })) - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - }) -}) - -describe("OpaqueFacade", () => { - it("keeps OpaqueFacade usable with struct schemas", () => { - const decoded = S.decodeSync(StructFacadeModel)({ value: "hello" }) - - expect(decoded).toStrictEqual({ value: "hello" }) - expect(S.encodeSync(StructFacadeModel)(decoded)).toStrictEqual({ value: "hello" }) - expect(S.is(StructFacadeModel)(decoded)).toBe(true) - expect(S.is(StructFacadeModel)({ value: 1 })).toBe(false) - }) - - it("keeps encoded-key struct facades on the struct path", () => { - const decoded = S.decodeSync(RenamedStructFacadeModel)({ external_name: "fran" }) - - expect(decoded).toStrictEqual({ internalName: "fran" }) - expect(decoded).not.toBeInstanceOf(RenamedStructFacadeModel) - expect(S.encodeSync(RenamedStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedStructFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedStructFacadeModel)({ external_name: "fran" })).toBe(false) - }) - - it("keeps encoded-key opaque struct facades on the struct path", () => { - const decoded = S.decodeSync(RenamedOpaqueStructFacadeModel)({ external_name: "fran" }) - - expect(decoded).toStrictEqual({ internalName: "fran" }) - expect(decoded).not.toBeInstanceOf(RenamedOpaqueStructFacadeModel) - expect(S.encodeSync(RenamedOpaqueStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedOpaqueStructFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedOpaqueStructFacadeModel)({ external_name: "fran" })).toBe(false) - }) - - it("decodes through the public facade class", () => { - const decoded = S.decodeSync(FacadeModel)({ value: "hello" }) - const decodedEffect = Effect.runSync(S.decodeUnknownEffect(FacadeModel)({ value: "effect" })) - - expect(decoded).toBeInstanceOf(FacadeModel) - expect(decodedEffect).toBeInstanceOf(FacadeModel) - expect(decoded).toBeInstanceOf(_FacadeModel) - expect(decoded.value).toBe("hello") - expect(decoded.upperValue).toBe("HELLO") - expect(decoded.decoratedValue).toBe("facade:hello") - expect(decoded.label()).toBe("facade:hello") - expect(FacadeModel.describe(decoded)).toBe("value:hello") - }) - - it("validates facade instances with S.is", () => { - const instance = new FacadeModel({ value: "hello" }) - - expect(instance).toBeInstanceOf(FacadeModel) - expect(instance.upperValue).toBe("HELLO") - expect(instance.label()).toBe("facade:hello") - expect(S.is(FacadeModel)(instance)).toBe(true) - expect(S.is(FacadeModel)(S.decodeSync(FacadeModel)({ value: "world" }))).toBe(true) - expect(S.is(FacadeModel)({ value: "plain" })).toBe(false) - expect(S.is(FacadeModel)({ value: 1 })).toBe(false) - }) - - it("decodes renamed keys through the public facade class", () => { - const decoded = S.decodeSync(RenamedFacadeModel)({ external_name: "fran" }) - - expect(decoded).toBeInstanceOf(RenamedFacadeModel) - expect(decoded).toBeInstanceOf(_RenamedFacadeModel) - expect(Object.getPrototypeOf(decoded)).toBe(RenamedFacadeModel.prototype) - expect(decoded.internalName).toBe("fran") - expect(decoded.upperName).toBe("FRAN") - expect(decoded.decoratedName).toBe("facade:fran") - expect(S.encodeSync(RenamedFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedFacadeModel)({ internalName: "plain" })).toBe(false) - expect(S.is(RenamedFacadeModel)({ external_name: "plain" })).toBe(false) - }) - - it("keeps generated struct facades decodable with public statics", () => { - const input: typeof RequestContext.Encoded = { - span: { traceId: "trace", spanId: "span", sampled: true }, - name: "operation", - locale: "en", - namespace: "tenant" - } - - const decoded = S.decodeSync(RequestContext)(input) - - expect(decoded).not.toBeInstanceOf(RequestContext) - expect(S.is(RequestContext)(decoded)).toBe(true) - expect(RequestContext.toMonitoring(decoded)).toStrictEqual({ operationName: "operation", locale: "en" }) - }) - - it("keeps error facades class-only", () => { - const makeStructErrorFacade = () => - AppSchema.OpaqueErrorFacadeClass< - { readonly value: string }, - { readonly value: string }, - { readonly value: string }, - never, - never - >()( - // Runtime guard: only class schemas are accepted (struct schemas throw). - AppSchema.Struct({ value: S.String }) as never - ) - - expect(makeStructErrorFacade).toThrow("OpaqueErrorFacadeClass requires a class schema") - }) -}) - -describe("Struct.copy", () => { - it("creates a new value with updated fields", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("accepts a function for updates", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, (a) => ({ b: a.b + 1 })) - - expect(copied).toEqual({ a: "hello", b: 2 }) - }) - - it("allows widening updates against the full struct type", () => { - const A = AppSchema.Struct({ - name: S.String, - state: S.Union([ - S.Struct({ _tag: S.tag("a"), a: S.String }), - S.Struct({ _tag: S.tag("b"), b: S.Number }) - ]) - }) - - const instance = A.make({ name: "x", state: { _tag: "a", a: "a" } }) - const copied = A.copy(instance, { state: { _tag: "b", b: 1 } }) - - expect(copied).toEqual({ name: "x", state: { _tag: "b", b: 1 } }) - }) - - it("copy function is preserved when using .annotate()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).annotate({ title: "A" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved when using .annotateKey()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).annotateKey({ title: "A" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved when using .mapFields()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).mapFields((f) => ({ ...f })) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved through chained calls", () => { - const A = AppSchema - .Struct({ a: S.String, b: S.Number }) - .annotate({ title: "A" }) - .annotateKey({ description: "test" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) -}) - -describe("TaggedStruct.copy", () => { - it("creates a new tagged value with updated fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, { radius: 10 }) - - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - expect(copied).not.toBe(instance) - }) -}) - -describe("TaggedError", () => { - it("InvalidStateError toString includes the message", () => { - const error = new InvalidStateError("something went wrong") - expect(error.toString()).toContain("something went wrong") - }) - - it("NotFoundError toString includes the message", () => { - const error = new NotFoundError({ type: "User", id: "123" }) - expect(error.toString()).toContain("Didn't find User") - expect(error.toString()).toContain("123") - }) - - it("ServiceUnavailableError toString includes the message", () => { - const error = new ServiceUnavailableError("service down") - expect(error.toString()).toContain("service down") - }) - - it("ValidationError toString includes the message", () => { - const error = new ValidationError({ errors: ["field required"] }) - expect(error.toString()).toContain("Validation failed") - expect(error.toString()).toContain("field required") - }) - - it("NotLoggedInError toString includes the message", () => { - const error = new NotLoggedInError("not logged in") - expect(error.toString()).toContain("not logged in") - }) - - it("LoginError toString includes the message", () => { - const error = new LoginError("login failed") - expect(error.toString()).toContain("login failed") - }) - - it("UnauthorizedError toString includes the message", () => { - const error = new UnauthorizedError("forbidden") - expect(error.toString()).toContain("forbidden") - }) - - it("OptimisticConcurrencyException toString includes the message", () => { - const error = new OptimisticConcurrencyException({ message: "conflict" }) - expect(error.toString()).toContain("conflict") - }) - - it("OptimisticConcurrencyException from details toString includes the message", () => { - const error = new OptimisticConcurrencyException({ type: "User", id: "123", code: 409 }) - expect(error.toString()).toContain("Existing User 123 record changed") - }) -}) - -describe("SpecialJsonSchema", () => { - it("nullable to optional — from NullOr", () => { - const nullableDecodedUndefinedEncoded = (schema: S.Top) => { - const isNullableSchema = "members" in schema - && globalThis.Array.isArray((schema as any).members) - && (schema as any).members.length === 2 - && (schema as any).members.some((member: any) => member.ast._tag === "Null") - - const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray : undefined - const innerSchema = nullableMembers - ? nullableMembers.find((member: any) => member.ast._tag !== "Null")! - : schema - - const nullableSchema = isNullableSchema ? schema : S.NullOr(schema) - - return nullableSchema.pipe( - S.encodeTo(S.optionalKey(innerSchema), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ) - } - - const fromNullOr = nullableDecodedUndefinedEncoded(S.NullOr(S.String)) - const structFromNullOr = S.Struct({ status: fromNullOr }) - - const encode = S.encodeUnknownSync(structFromNullOr as any) - const encodedNull = encode({ status: null }) - expect("status" in encodedNull).toBe(false) - expect(encode({ status: "test" })).toStrictEqual({ status: "test" }) - - const decode = S.decodeUnknownSync(structFromNullOr as any) - expect(decode({})).toStrictEqual({ status: null }) - expect(decode({ status: "test" })).toStrictEqual({ status: "test" }) - - const doc = specialJsonSchemaDocument(structFromNullOr) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "status": { "type": "string" } - } - }, - definitions: {} - }) - }) - - it("identifies X universally — deduplicates same-fingerprint references", () => { - const X = S.String.annotate({ title: "X", identifier: "X" }) - - const s = S.Struct({ - a: S.NullOr(X).pipe( - S.encodeTo(S.optionalKey(X), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ), - b: S.NullOr(X).pipe( - S.encodeTo(S.optionalKey(X), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ), - c: S.NullOr(X), - d: X, - e: X.pipe(S.optionalKey) - }) - - const doc = specialJsonSchemaDocument(s) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "a": { "$ref": "#/$defs/X" }, - "b": { "$ref": "#/$defs/X" }, - "c": { - "anyOf": [ - { "$ref": "#/$defs/X" }, - { "type": "null" } - ] - }, - "d": { "$ref": "#/$defs/X" }, - "e": { "$ref": "#/$defs/X" } - }, - "required": ["c", "d"] - }, - definitions: { - X: { - "type": "string", - "title": "X" - } - } - }) - }) - - it("shared annotated schema via helper — deduplicates", () => { - const X = S.String.annotate({ title: "X", identifier: "X" }) - - const cache = new WeakMap() - const nullableDecodedUndefinedEncoded = (schema: S.Top) => { - const isNullableSchema = "members" in schema - && globalThis.Array.isArray((schema as any).members) - && (schema as any).members.length === 2 - && (schema as any).members.some((member: any) => member.ast._tag === "Null") - - const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray : undefined - const innerSchema = nullableMembers - ? nullableMembers.find((member: any) => member.ast._tag !== "Null")! - : schema - - const cached = cache.get(innerSchema.ast) - if (cached !== undefined) return cached - - const nullableSchema = isNullableSchema ? schema : S.NullOr(schema) - const out = nullableSchema.pipe( - S.encodeTo(S.optionalKey(innerSchema), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ) - - cache.set(innerSchema.ast, out) - return out - } - - const structWithShared = S.Struct({ - a: nullableDecodedUndefinedEncoded(X), - b: nullableDecodedUndefinedEncoded(S.NullOr(X)), - c: S.NullOr(X), - d: X, - e: X.pipe(S.optionalKey) - }) - - const doc = specialJsonSchemaDocument(structWithShared) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "a": { "$ref": "#/$defs/X" }, - "b": { "$ref": "#/$defs/X" }, - "c": { - "anyOf": [ - { "$ref": "#/$defs/X" }, - { "type": "null" } - ] - }, - "d": { "$ref": "#/$defs/X" }, - "e": { "$ref": "#/$defs/X" } - }, - "required": ["c", "d"] - }, - definitions: { - X: { - "type": "string", - "title": "X" - } - } - }) - }) -}) - -describe("SpecialOpenApi", () => { - it("deduplicates identical components.schemas entries with same base identifier", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: { - "/foo": { - get: { - responses: { - 200: { - content: { - "application/json": { - schema: { $ref: "#/components/schemas/X" } - } - } - } - } - } - }, - "/bar": { - get: { - responses: { - 200: { - content: { - "application/json": { - schema: { $ref: "#/components/schemas/X1" } - } - } - } - } - } - } - }, - components: { - schemas: { - X: { type: "string", title: "X" }, - X1: { type: "string", title: "X" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - // X1 should be removed, and $ref to X1 rewritten to X - expect(result.components.schemas).toStrictEqual({ - X: { type: "string", title: "X" } - }) - expect( - result.paths["/bar"].get.responses[200].content["application/json"].schema - ) - .toStrictEqual({ $ref: "#/components/schemas/X" }) - }) - - it("does not deduplicate entries with different representations", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - X: { type: "string", title: "X" }, - X1: { type: "number", title: "X" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - // Both should remain since they have different representations - expect(result.components.schemas).toStrictEqual({ - X: { type: "string", title: "X" }, - X1: { type: "number", title: "X" } - }) - }) - - it("returns spec unchanged when no duplicates exist", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Foo: { type: "string" }, - Bar: { type: "number" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) - expect(result).toStrictEqual(spec) - }) - - it("rewrites nested $ref pointers in allOf/anyOf/oneOf", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: { - "/baz": { - post: { - requestBody: { - content: { - "application/json": { - schema: { - anyOf: [ - { $ref: "#/components/schemas/Y1" }, - { type: "null" } - ] - } - } - } - } - } - } - }, - components: { - schemas: { - Y: { type: "object", properties: { name: { type: "string" } } }, - Y1: { type: "object", properties: { name: { type: "string" } } } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas).toStrictEqual({ - Y: { type: "object", properties: { name: { type: "string" } } } - }) - expect( - result.paths["/baz"].post.requestBody.content["application/json"].schema.anyOf[0] - ) - .toStrictEqual({ $ref: "#/components/schemas/Y" }) - }) - - it("rewrites $ref pointers inside definitions themselves", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Inner: { type: "string" }, - Inner1: { type: "string" }, - Outer: { - type: "object", - properties: { - field: { $ref: "#/components/schemas/Inner1" } - } - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(Object.keys(result.components.schemas)).toStrictEqual(["Inner", "Outer"]) - expect(result.components.schemas.Outer.properties.field).toStrictEqual({ - $ref: "#/components/schemas/Inner" - }) - }) - - it("handles spec without components gracefully", () => { - const spec = { openapi: "3.1.0", info: { title: "Test", version: "1.0" }, paths: {} } - const result = deduplicateOpenApiSchemas(spec) - expect(result).toStrictEqual(spec) - }) - - it("flattens allOf in components.schemas", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - PositiveInt: { - type: "integer", - allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }] - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.PositiveInt).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0, - title: "PositiveInt" - }) - }) - - it("does not flatten allOf containing $ref entries", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Composed: { - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - }, - Base: { type: "object", properties: { id: { type: "string" } } } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.Composed).toStrictEqual({ - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - }) - }) - - it("does not flatten allOf entries that define their own type", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Mixed: { - type: "object", - allOf: [{ type: "string", minLength: 1 }] - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.Mixed).toStrictEqual({ - type: "object", - allOf: [{ type: "string", minLength: 1 }] - }) - }) -}) - -describe("flattenSimpleAllOf", () => { - it("flattens constraint-only allOf into parent with type", () => { - const input = { - type: "integer", - allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0, - title: "PositiveInt" - }) - }) - - it("flattens string type with multiple constraints", () => { - const input = { - type: "string", - allOf: [ - { minLength: 1, maxLength: 255 }, - { title: "NonEmptyString255" } - ] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 255, - title: "NonEmptyString255" - }) - }) - - it("does not flatten allOf with $ref", () => { - const input = { - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual(input) - }) - - it("does not flatten allOf entries with their own type", () => { - const input = { - type: "object", - allOf: [{ type: "string", minLength: 1 }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual(input) - }) - - it("allOf entry wins on property conflict", () => { - const input = { - type: "integer", - title: "OldTitle", - allOf: [{ title: "NewTitle", minimum: 0 }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "integer", - title: "NewTitle", - minimum: 0 - }) - }) -}) - -describe("Post-processing integration — real Effect Schema types", () => { - it("PositiveInt — allOf flattened, no wrapping", () => { - const doc = specialJsonSchemaDocument(AppSchema.PositiveInt) - expect(doc.definitions["PositiveInt"]).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0 - }) - }) - - it("NonEmptyString255 — multiple allOf constraints merged", () => { - const doc = specialJsonSchemaDocument(AppSchema.NonEmptyString255) - expect(doc.definitions["NonEmptyString255"]).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 255 - }) - }) - - it("NullOr(NonEmptyString64k) — null preserved in anyOf, allOf flattened in definition", () => { - const schema = S.Struct({ note: S.NullOr(AppSchema.NonEmptyString64k) }) - const doc = specialJsonSchemaDocument(schema) - - // null variant preserved (correct JSON Schema for NullOr) - expect(doc.schema).toStrictEqual({ - type: "object", - additionalProperties: true, - properties: { - note: { - anyOf: [ - { $ref: "#/$defs/NonEmptyString64k" }, - { type: "null" } - ] - } - }, - required: ["note"] - }) - - // allOf flattened in the referenced definition - expect(doc.definitions["NonEmptyString64k"]).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 65536 - }) - }) - - it("NonNegativeInt — allOf flattened", () => { - const doc = specialJsonSchemaDocument(AppSchema.NonNegativeInt) - expect(doc.definitions["NonNegativeInt"]).toStrictEqual({ - type: "integer", - minimum: 0 - }) - }) - - it("NullOr union flattens nested anyOf members", () => { - const A = S.String.annotate({ identifier: "A" }) - const B = S.Boolean.annotate({ identifier: "B" }) - const schema = S.Struct({ - value: S.NullOr(S.Union([A, B])) - }) - const doc = specialJsonSchemaDocument(schema) - const valueProp = (doc.schema as Record)["properties"]["value"] - - expect(valueProp).toStrictEqual({ - anyOf: [ - { $ref: "#/$defs/A" }, - { $ref: "#/$defs/B" }, - { type: "null" } - ] - }) - }) -}) - -describe("flattenNestedAnyOf", () => { - it("flattens nested anyOf with no sibling keys", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }, { type: "number" }] }, - { type: "null" } - ] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - anyOf: [ - { type: "string" }, - { type: "number" }, - { type: "null" } - ] - }) - }) - - it("does not flatten anyOf entry with sibling keys", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }], title: "X" }, - { type: "null" } - ] - } - // The inner anyOf is not flattened into the outer (sibling "title" prevents it), - // but the single-element inner anyOf is unwrapped within the entry itself - expect(flattenNestedAnyOf(input)).toStrictEqual({ - anyOf: [ - { type: "string", title: "X" }, - { type: "null" } - ] - }) - }) - - it("unwraps anyOf with single item after flattening", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }] } - ] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ type: "string" }) - }) - - it("unwraps anyOf with single item, merging sibling properties", () => { - const input = { - title: "MyField", - anyOf: [{ type: "string" }] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - title: "MyField", - type: "string" - }) - }) - - it("recurses into nested objects", () => { - const input = { - properties: { - field: { - anyOf: [ - { anyOf: [{ $ref: "#/defs/A" }, { $ref: "#/defs/B" }] }, - { type: "null" } - ] - } - } - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - properties: { - field: { - anyOf: [ - { $ref: "#/defs/A" }, - { $ref: "#/defs/B" }, - { type: "null" } - ] - } - } - }) - }) - - it("passes through non-objects unchanged", () => { - expect(flattenNestedAnyOf(null)).toBe(null) - expect(flattenNestedAnyOf(42)).toBe(42) - expect(flattenNestedAnyOf("hello")).toBe("hello") - }) -}) diff --git a/packages/effect-app/test/utils.test.ts b/packages/effect-app/test/utils.test.ts deleted file mode 100644 index 2fe525b99..000000000 --- a/packages/effect-app/test/utils.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import * as S from "effect-app/Schema" -import { test } from "vitest" -import { copyOrigin } from "../src/utils.js" - -test("works with class", () => { - class Banana { - name: string - state: { a: string; _tag: "a" } | { b: number; _tag: "b" } - - constructor(params: { name: string; state: { a: string; _tag: "a" } | { b: number; _tag: "b" } }) { - this.name = params.name - this.state = params.state - } - } - - const copyBanana = copyOrigin(Banana) - - const original = new Banana({ name: "banana", state: { a: "a", _tag: "a" } }) - - const res1 = copyBanana( - original, - (_) => ({ state: { b: 1, _tag: "b" } }) - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res1).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res1).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res1)).toEqual(Banana.prototype) - - const res2 = copyBanana( - original, - { state: { b: 1, _tag: "b" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res2).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res2).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res2)).toEqual(Banana.prototype) - - const res3 = copyBanana( - res2, - { state: { a: "a", _tag: "a" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res3).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } - }>() - - expect(res3).toEqual({ - name: "banana", - state: { a: "a", _tag: "a" } - }) - - expect(Object.getPrototypeOf(res3)).toEqual(Banana.prototype) - - copyBanana( - original, - // @ts-expect-error extraProp is not a valid property of Banana - { name: "string", extraProp: "whatever" } - ) - - copyBanana( - // @ts-expect-error extraProp is not a valid property of Banana - original, - { extraProp: "whatever" } - ) - - const res4 = copyBanana( - original, - (o) => o - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res4).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } | { - b: number - _tag: "b" - } - }>() -}) - -test("works with schema class", () => { - class Banana extends S.Class("Banana")({ - name: S.String, - state: S.Union([ - S.Struct({ a: S.String, _tag: S.tag("a") }), - S.Struct({ b: S.Finite, _tag: S.tag("b") }) - ]) - }) {} - - const copyBanana = copyOrigin(Banana) - - const original = new Banana({ name: "banana", state: { a: "a", _tag: "a" } }) - - const res1 = copyBanana( - original, - (_) => ({ state: { b: 1, _tag: "b" } }) - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res1).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res1).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res1)).toEqual(Banana.prototype) - - const res2 = copyBanana( - original, - { state: { b: 1, _tag: "b" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res2).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res2).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res2)).toEqual(Banana.prototype) - - const res3 = copyBanana( - res2, - { state: { a: "a", _tag: "a" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res3).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } - }>() - - expect(res3).toEqual({ - name: "banana", - state: { a: "a", _tag: "a" } - }) - - expect(Object.getPrototypeOf(res3)).toEqual(Banana.prototype) - - copyBanana( - original, - // @ts-expect-error extraProp is not a valid property of Banana - { name: "string", extraProp: "whatever" } - ) - - copyBanana( - // @ts-expect-error extraProp is not a valid property of Banana - original, - { extraProp: "whatever" } - ) - - const res4 = copyBanana( - original, - (o) => o - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res4).toEqualTypeOf<{ - name: string - state: { - readonly a: string - readonly _tag: "a" - } | { - readonly b: number - readonly _tag: "b" - } - }>() -}) diff --git a/packages/effect-app/tsconfig.base.json b/packages/effect-app/tsconfig.base.json deleted file mode 100644 index 832135d45..000000000 --- a/packages/effect-app/tsconfig.base.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "extends": "@tsconfig/strictest/tsconfig.json", - "compilerOptions": { - "module": "Node16", - "lib": [ - "ES2023" - ], - "target": "ES2022", - // inline source maps stay better in sync with the source, which should aid with watchers. - // "sourceMap": true, - "inlineSourceMap": true, - "incremental": true, - "composite": true, - "declarationMap": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "noImplicitAny": true, - "useUnknownInCatchVariables": true, - // This is not desirable, as it's useful in generators. - "noImplicitReturns": false, - "noImplicitThis": true, - "resolveJsonModule": true, - "moduleResolution": "Node16", - "noErrorTruncation": true, - "forceConsistentCasingInFileNames": true, - "rewriteRelativeImportExtensions": true, - "allowImportingTsExtensions": true - }, - "watchOptions": { - // Use native file system events for files and directories - // This actually detect changes in parent directories ... not great! - // however it seems that the response is very fast on the non-related projects figuring out their files are unchanged... - "watchFile": "useFsEvents", - "watchDirectory": "useFsEvents", - // // Poll files for updates more frequently - // // when they're updated a lot. - "fallbackPolling": "dynamicPriority", - "excludeDirectories": [ - "**/node_modules", - "**/dist", - "**/.build", - "**/.git", - "**/.data", - "**/.logs", - "**/.*" - ], - "excludeFiles": [ - "**/*.tmp", - "openapi.json", - "*.json" - ] - }, - "files": [], - "include": [], - "exclude": [ - "**/node_modules", - "**/build", - "**/dist", - "**/.*" - ] -} diff --git a/packages/effect-app/tsconfig.json b/packages/effect-app/tsconfig.json deleted file mode 100644 index 8565116ac..000000000 --- a/packages/effect-app/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": {}, - "extends": "../../tsconfig.base.json", - "references": [ - { - "path": "./tsconfig.src.json" - }, - { - "path": "./tsconfig.test.json" - } - ] -} diff --git a/packages/effect-app/tsconfig.src.json b/packages/effect-app/tsconfig.src.json deleted file mode 100644 index 872c35832..000000000 --- a/packages/effect-app/tsconfig.src.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext" - ], - "tsBuildInfoFile": "./dist/.tsbuildinfo", - "esModuleInterop": true, - "rootDir": "./src", - // keep in here, cause madge can't detect it from extended tsconfig - "moduleResolution": "Node16", - "outDir": "./dist" - }, - "include": [ - "./src/**/*.ts" - ], - "exclude": [ - "./dist", - "*.test.ts", - "node_modules", - "build", - "lib", - "dist", - "**/*.d.ts.map", - ".git", - ".data", - "**/.*", - "**/*.tmp" - ], - "references": [] -} diff --git a/packages/effect-app/tsconfig.test.json b/packages/effect-app/tsconfig.test.json deleted file mode 100644 index c071d42d0..000000000 --- a/packages/effect-app/tsconfig.test.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "extends": "./tsconfig.src.json", - "compilerOptions": { - "rootDir": "./test", - "outDir": "./test/dist", - "tsBuildInfoFile": "./test/dist/.tsbuildinfo", - "types": [ - "node", - "vitest/globals" - ] - }, - "include": [ - "./test/**/*.ts" - ], - "exclude": [ - "./dist", - "./test/dist", - "node_modules", - "build", - "lib", - "dist", - "**/*.d.ts.map", - ".git", - ".data", - "**/.*", - "**/*.tmp" - ], - "references": [ - { - "path": "./tsconfig.src.json" - } - ] -} diff --git a/packages/effect-app/vitest.config.ts b/packages/effect-app/vitest.config.ts deleted file mode 100644 index 85903fa64..000000000 --- a/packages/effect-app/vitest.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig(makeConfig(__dirname)) diff --git a/repos/effect/packages/effect/ARBITRARY.md b/packages/effect/ARBITRARY.md similarity index 100% rename from repos/effect/packages/effect/ARBITRARY.md rename to packages/effect/ARBITRARY.md diff --git a/repos/effect/packages/effect/CHANGELOG.md b/packages/effect/CHANGELOG.md similarity index 95% rename from repos/effect/packages/effect/CHANGELOG.md rename to packages/effect/CHANGELOG.md index a1d0f6816..8a345116a 100644 --- a/repos/effect/packages/effect/CHANGELOG.md +++ b/packages/effect/CHANGELOG.md @@ -1,5 +1,201 @@ # effect +## 4.0.2 + +### Patch Changes + +- [#8870](https://github.com/Effect-TS/effect/pull/8870) [`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1) Thanks @Makisuo! - Update AI tracing to use current OpenTelemetry GenAI conventions: + + - Replace `gen_ai.system` with `gen_ai.provider.name`. The `system` option, `BaseAttributes` and `WellKnownSystem` are removed in favor of `provider: { name }`, `ProviderAttributes` and `WellKnownProviderName`. + - Remove `gen_ai.token.type`, which is not a span attribute in the current conventions, along with the `token` option, `TokenAttributes` and the `AllAttributes` types. + - `LanguageModel` spans are client spans and record `gen_ai.output.type` (`text` or `json`) for every provider. The OpenAI `responseFormat` telemetry option and `WellKnownResponseFormat` are removed. + - Move OpenAI service-tier and system-fingerprint attributes from `gen_ai.openai.*` to `openai.*`. + - Rename `concurrency`, `toolChoice` and `objectName` span attributes to `effect.ai.concurrency`, `effect.ai.tool_choice` and `effect.ai.object_name`. Tool choices record the resolved value, defaulting to `"auto"`; object choices are JSON-encoded. + - Include cached tokens in Anthropic `gen_ai.usage.input_tokens` and report cache reads and writes as `gen_ai.usage.cache_read.input_tokens` and `gen_ai.usage.cache_write.input_tokens`. The duplicate Anthropic `cacheCreationInputTokens` and `cacheReadInputTokens` response options are removed. + +- [#8849](https://github.com/Effect-TS/effect/pull/8849) [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63) Thanks @gcanti! - Fix Arbitrary property checks, schema generation, and shrinking swallowing defects or interruption when they accompany typed failures. Propagate the defect and interruption reasons with their annotations, removing typed failures from mixed causes to preserve the existing error types. + +- [#8760](https://github.com/Effect-TS/effect/pull/8760) [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105) Thanks @fubhy! - Include runtime layer errors in the result type of `AtomRuntime.subscriptionRef`. + +- [#8820](https://github.com/Effect-TS/effect/pull/8820) [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3) Thanks @jarrednorrisdev! - Preserve the URL hash and omit empty query strings when writing `Atom.searchParam`. + +- [#8771](https://github.com/Effect-TS/effect/pull/8771) [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058) Thanks @jarrednorrisdev! - Prevent `Atom.windowFocusSignal` and `Atom.refreshOnWindowFocus` from throwing when `window` is undefined. + +- [#8837](https://github.com/Effect-TS/effect/pull/8837) [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91) Thanks @tim-smart! - Fix a Cache.get interruption race that could leave an abandoned lookup running and its key stuck in the cache. + +- [#8778](https://github.com/Effect-TS/effect/pull/8778) [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584) Thanks @fubhy! - Fix a stack overflow when many consecutive failure handlers throw synchronously. The thrown defect is now captured as the fiber's exit and pending finalizers still run. + +- [#8838](https://github.com/Effect-TS/effect/pull/8838) [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6) Thanks @tim-smart! - Derive `Channel.decodeText` options from the `TextDecoder` constructor so declarations work with Node-only types without requiring the DOM library. + +- [#8758](https://github.com/Effect-TS/effect/pull/8758) [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5) Thanks @fubhy! - Fix `Channel.mapInput` data-last overload to preserve channel requirements and add the mapping function's requirements. + +- [#8757](https://github.com/Effect-TS/effect/pull/8757) [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023) Thanks @fubhy! - Fix `Channel.runIntoPubSub` to include the channel's error type in the returned `Effect`. + +- [#8810](https://github.com/Effect-TS/effect/pull/8810) [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02) Thanks @fubhy! - Fix compiled Struct parsers sharing mutable output when the same suspended Effect is executed more than once. + +- [#8808](https://github.com/Effect-TS/effect/pull/8808) [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740) Thanks @fubhy! - Preserve provider defects and interruption reasons during Config parsing, and prevent Config.orElse from recovering mixed causes. + +- [#8877](https://github.com/Effect-TS/effect/pull/8877) [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff) Thanks @jayhemnani9910! - Preserve separators inside `Config.Record` values, including base64 padding in OTLP headers. + +- [#8832](https://github.com/Effect-TS/effect/pull/8832) [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204) Thanks @tim-smart! - Add image input to `DecisionModel.decide`. Providers opt in with `supportsImages: true` and receive images through `ProviderOptions.images`. Models without image support reject nonempty image input with `InvalidUserInputError`. + +- [#8765](https://github.com/Effect-TS/effect/pull/8765) [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319) Thanks @fubhy! - Fix `ExecutionPlan` to preserve errors and service requirements from layers, predicates, and schedules across fallback steps. Provide captured services to schedules when using `captureRequirements`. + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. + +- [#8814](https://github.com/Effect-TS/effect/pull/8814) [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3) Thanks @fubhy! - Fix Map and Set schema equivalences incorrectly treating proper subsets as equal. + +- [#8804](https://github.com/Effect-TS/effect/pull/8804) [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302) Thanks @fubhy! - Fix stack overflow when many equal requests share a pending `RequestResolver.withCache` entry. + +- [#8805](https://github.com/Effect-TS/effect/pull/8805) [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9) Thanks @fubhy! - Allow interruption while waiting for permits or locks in TxSemaphore and TxReentrantLock helpers, while preserving automatic release after acquisition. + +- [#8586](https://github.com/Effect-TS/effect/pull/8586) [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c) Thanks @Marve10s! - Allow spans without an Effect parent to inherit active OpenTelemetry spans created outside Effect. + +- [#8799](https://github.com/Effect-TS/effect/pull/8799) [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4) Thanks @fubhy! - Preserve finalizer defects in `Effect.repeat` and `Effect.schedule`. Prevent `Effect.retry` from retrying failures containing defects or interruptions. + +- [#8753](https://github.com/Effect-TS/effect/pull/8753) [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6) Thanks @jayhemnani9910! - Fix `Toml.parse` to trim an immediate opening CRLF in basic and literal multiline strings, matching its existing LF handling. + +- [#8752](https://github.com/Effect-TS/effect/pull/8752) [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535) Thanks @jayhemnani9910! - Fix `Yaml.parse` block scalar chomping to preserve trailing line breaks with keep (`|+`, `>+`) and avoid adding line breaks to empty or unterminated scalars. + +- [#8815](https://github.com/Effect-TS/effect/pull/8815) [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676) Thanks @fubhy! - Fix Array concatenation reducers sharing mutable empty results across calls and reducer instances. + +- [#8811](https://github.com/Effect-TS/effect/pull/8811) [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde) Thanks @fubhy! - Fix Standard Schema and Standard JSON Schema adapters to use the child schema rather than an inherited parent adapter. + +- [#8792](https://github.com/Effect-TS/effect/pull/8792) [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db) Thanks @fubhy! - Fix `Stream.peel` dropping the sink's unconsumed elements from a partially consumed chunk. + +- [#8809](https://github.com/Effect-TS/effect/pull/8809) [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e) Thanks @fubhy! - Fix suspended Schema union parsers reusing successes and issues when the same decode or encode effect is run again. + +- [#8742](https://github.com/Effect-TS/effect/pull/8742) [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10) Thanks @wouter173! - Fix the data-last overload of `AsyncResult.flatMap` to correctly infer a changed success type. + +- [#8830](https://github.com/Effect-TS/effect/pull/8830) [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6) Thanks @devangpratap! - Fix `HttpClient` retaining unread responses on runtimes without `FinalizationRegistry`, such as Hermes. + +- [#8772](https://github.com/Effect-TS/effect/pull/8772) [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f) Thanks @juliusmarminge! - Read `HttpClientResponse.cookies` on platforms whose `Headers` has no `getSetCookie`, such as React Native, instead of throwing. + +- [#8873](https://github.com/Effect-TS/effect/pull/8873) [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4) Thanks @tim-smart! - Align HTTP tracing span names, response error status, server addresses and ports with OpenTelemetry conventions. Make header capture opt-in and export homogeneous primitive arrays as array attributes in `@effect/opentelemetry`. + +- [#8759](https://github.com/Effect-TS/effect/pull/8759) [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8) Thanks @fubhy! - Fix `HttpServer` service `serve` to infer required services from the middleware-transformed app. + +- [#8821](https://github.com/Effect-TS/effect/pull/8821) [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f) Thanks @Makisuo! - Mark `HttpMiddleware.tracer` server spans as failed for 5xx responses, including rendered errors, without changing the application exit or existing span failure causes. + +- [#8852](https://github.com/Effect-TS/effect/pull/8852) [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e) Thanks @effect-bot! - Report the `101` status of a successful WebSocket upgrade to server middleware such as `HttpMiddleware.logger` and `HttpMiddleware.tracer`, instead of the handler's discarded response status. + +- [#8767](https://github.com/Effect-TS/effect/pull/8767) [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf) Thanks @fubhy! - Fix `HttpApiClient` and `AtomHttpApi` result types for union response modes while preserving decoded-only results when the mode is omitted. + +- [#8858](https://github.com/Effect-TS/effect/pull/8858) [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60) Thanks @jayhemnani9910! - Fix `HttpApi` rejecting single-value array fields in form-urlencoded and GET payloads. + +- [#8834](https://github.com/Effect-TS/effect/pull/8834) [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be) Thanks @tim-smart! - Return 500 and report HTTP API response encoding failures to ErrorReporter, while keeping request decoding failures at 400 and unreported. Preserve response failures as HttpApiSchemaError values for schema-error middleware. + +- [#8875](https://github.com/Effect-TS/effect/pull/8875) [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d) Thanks @tim-smart! - expose more of the atom registry apis + +- [#8836](https://github.com/Effect-TS/effect/pull/8836) [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9) Thanks @tim-smart! - Cache the Kubernetes service-account token for one minute so K8sHttpClient picks up rotated credentials without reading the token file on every request. + +- [#8773](https://github.com/Effect-TS/effect/pull/8773) [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce) Thanks @juliusmarminge! - Add opt-in `allowSessionTermination` to `McpServer.layerHttp`. DELETE ends the session and interrupts its active requests; later requests with that session id return `404`. + + Fix an RPC cancellation race by registering request fibers before their handlers run. + +- [#8775](https://github.com/Effect-TS/effect/pull/8775) [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b) Thanks @RhysSullivan! - `McpServer.layerHttp` uses the negotiated protocol version when a session request omits `MCP-Protocol-Version`. Unsupported or mismatched protocol versions, missing required `MCP-Session-Id` headers, and `initialize` requests with a session header now return JSON-RPC error bodies. + +- [#8842](https://github.com/Effect-TS/effect/pull/8842) [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad) Thanks @brandon-julio-t! - Fix MCP toolkit registration to require tool handler and schema services. + +- [#8864](https://github.com/Effect-TS/effect/pull/8864) [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710) Thanks @Makisuo! - Leave interrupt-only spans `Unset` with an `effect.fiber.interrupted: true` attribute instead of `Ok` with an "Interrupted" description and the `span.label` / `status.interrupted` attributes. Successful spans keep `Ok`; failures keep `Error` and their exception events. + + `Cause.prettyErrors` no longer captures an internal stack frame for non-object failures such as `Effect.fail("boom")`; the stack is now the message plus the span frame, when available. This affects the `exception.stacktrace` exported by both tracers and the output of `Cause.pretty`. + +- [#8868](https://github.com/Effect-TS/effect/pull/8868) [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb) Thanks @Makisuo! - Align OTLP and `@effect/opentelemetry` logs, traces, and resources with OpenTelemetry conventions. + +- [#8869](https://github.com/Effect-TS/effect/pull/8869) [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03) Thanks @Makisuo! - OtlpMetrics exports each summary as one OTLP Summary metric named `` instead of `_quantiles`, `_count` and `_sum` Sum metrics. Summaries are always cumulative. Common unit names such as `milliseconds` are mapped to UCUM, and bigint values beyond 2^53 keep their precision. + +- [#8756](https://github.com/Effect-TS/effect/pull/8756) [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9) Thanks @creatifcoding! - Fix PostgreSQL `SqlEventJournal` writes by storing entry and remote IDs as `BYTEA`, and decode `BIGINT` timestamps returned as strings or bigints. + + Existing tables with `UUID` columns are not migrated automatically. Recreate both journal tables only after confirming they are empty, or externally migrate `id`, `remote_id`, and `entry_id` to `BYTEA` while preserving existing data. + +- [#8762](https://github.com/Effect-TS/effect/pull/8762) [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3) Thanks @fubhy! - Include completion-handler services in the requirements of `McpServer.registerPrompt` and `McpServer.prompt`, including optional handlers. + +- [#8819](https://github.com/Effect-TS/effect/pull/8819) [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878) Thanks @mikearnaldi! - Prevent message loss when `Queue.take`, `takeAll`, `takeN`, `takeBetween`, `poll`, and `clear` are interrupted at a scheduler yield. + +- [#8816](https://github.com/Effect-TS/effect/pull/8816) [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12) Thanks @fubhy! - Fix `Channel.merge` hanging or succeeding when a side finalizer fails, preserving both usage and finalizer failures. + +- [#8798](https://github.com/Effect-TS/effect/pull/8798) [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42) Thanks @fubhy! - Fix `Channel.mergeAll` and concurrent `Channel.flatMap` dropping inner scope finalizer defects, preserving both usage and release failures. + +- [#8800](https://github.com/Effect-TS/effect/pull/8800) [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f) Thanks @fubhy! - Fix `Cron.next` returning past occurrences or exhausting its search limit when starting in a repeated DST interval. + +- [#8777](https://github.com/Effect-TS/effect/pull/8777) [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f) Thanks @fubhy! - Fix child fibers reusing completed transaction journals so later transactions commit their writes. + +- [#8784](https://github.com/Effect-TS/effect/pull/8784) [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade) Thanks @fubhy! - Fix FiberHandle losing ownership of fibers started by a synchronous replacement finalizer. + +- [#8782](https://github.com/Effect-TS/effect/pull/8782) [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e) Thanks @fubhy! - Fix `Formatter.formatJson` redaction of getter-returned values with `toJSON` without invoking getters twice. + +- [#8776](https://github.com/Effect-TS/effect/pull/8776) [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321) Thanks @fubhy! - Fix `Effect.effectify` so throwing error mappers become defects instead of escaping asynchronous callbacks and leaving fibers suspended. + +- [#8789](https://github.com/Effect-TS/effect/pull/8789) [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23) Thanks @fubhy! - Fix stack overflows when PartitionedSemaphore hands permits through long queues of synchronous tasks. + +- [#8791](https://github.com/Effect-TS/effect/pull/8791) [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf) Thanks @fubhy! - Fix `Queue.clear` swallowing failures combined with `Cause.Done`. + +- [#8785](https://github.com/Effect-TS/effect/pull/8785) [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2) Thanks @fubhy! - Fix Queue consumers defecting and messages being buffered after reentrant shutdown while resuming blocked producers. + +- [#8779](https://github.com/Effect-TS/effect/pull/8779) [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469) Thanks @fubhy! - Mask interruption before invoking scope finalizer callbacks so singleton cleanup completes when its callback interrupts the closing fiber. + +- [#8795](https://github.com/Effect-TS/effect/pull/8795) [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec) Thanks @fubhy! - Fix `Stream.zipLatest`, `zipLatestWith`, and `zipLatestAll` hanging when an input completes without emitting. + +- [#8780](https://github.com/Effect-TS/effect/pull/8780) [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29) Thanks @fubhy! - Preserve failures raised while a transaction is retrying instead of rerunning the transaction. + +- [#8833](https://github.com/Effect-TS/effect/pull/8833) [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec) Thanks @tim-smart! - Return HTTP 200 with an empty body for empty RPC POST requests using framed serialization. + +- [#8867](https://github.com/Effect-TS/effect/pull/8867) [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c) Thanks @Makisuo! - RPC client and server spans now set span kind `client` / `server` and record the OpenTelemetry `rpc.system.name` (`effect_rpc`) and `rpc.method` attributes. User `spanAttributes` still override them. + + Default span names are now the RPC method, for example `Echo.Ping` instead of `RpcClient.Echo.Ping`. Update dashboards, alerts or samplers that match the old names, or pass `spanPrefix: "RpcClient"` / `spanPrefix: "RpcServer"` to keep them. Spans with an explicit `spanPrefix` keep their existing names. + +- [#8835](https://github.com/Effect-TS/effect/pull/8835) [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4) Thanks @tim-smart! - Fix RPC request defects breaking subsequent SchemaBinary calls after an unknown request tag or payload decoding failure. + +- [#8825](https://github.com/Effect-TS/effect/pull/8825) [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c) Thanks @tim-smart! - Add configurable `pingInterval` and `pingTimeout` to RPC socket clients, counting any decoded server frame as liveness. Forward `retryPolicy` through `layerProtocolSocket`. The default ping interval and timeout remain 5 seconds. + +- [#8748](https://github.com/Effect-TS/effect/pull/8748) [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac) Thanks @gcanti! - Expose the selected tag property key through `tag` on schemas returned by `Schema.toTaggedUnion` and `Schema.TaggedUnion`, closes [#8681](https://github.com/Effect-TS/effect/issues/8681). + +- [#8764](https://github.com/Effect-TS/effect/pull/8764) [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b) Thanks @fubhy! - Fix the data-last `Sink.catchCause` overload to return the handler's error type instead of the original error type. + +- [#8878](https://github.com/Effect-TS/effect/pull/8878) [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf) Thanks @jayhemnani9910! - Fix `Stream.slidingSize` dropping elements from the final partial window when windows overlap. + +- [#8855](https://github.com/Effect-TS/effect/pull/8855) [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761) Thanks @tim-smart! - Fix SQL unencrypted event-log notifications to publish in sequence order after commit, without missing startup rows or publishing rolled-back writes. + +- [#8866](https://github.com/Effect-TS/effect/pull/8866) [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30) Thanks @Makisuo! - Align SQL tracing with OpenTelemetry database conventions: + + - Name statement spans by `db.namespace`, `server.address[:server.port]` or `db.system.name`, falling back to `sql.execute`. The rules are exported as `Statement.spanName` and `Statement.spanTarget`. + - Record Effect execution methods in `effect.sql.method` instead of `db.operation.name`. + - Use internal transaction spans with `effect.sql.transaction.commit|savepoint|rollback` events. + - Name D1 batches of two or more statements `BATCH`, or `BATCH {target}` when a target is set, with `db.operation.name` set to `BATCH` and `db.operation.batch.size`. + +- [#8857](https://github.com/Effect-TS/effect/pull/8857) [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0) Thanks @jayhemnani9910! - Fix `Stream.groupBy`, `Stream.groupByKey`, `Stream.partition`, and `Stream.partitionEffect` hanging when a substream consumer stops early by shutting down its queue when the consumer finishes. + +- [#8794](https://github.com/Effect-TS/effect/pull/8794) [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea) Thanks @fubhy! - Fix `Stream.tapSink` swallowing sink failures after end-of-stream. + +- [#8817](https://github.com/Effect-TS/effect/pull/8817) [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23) Thanks @fubhy! - Fix `Channel.callback` and `Channel.callbackArray` losing registration effect failures and defects. + +- [#8796](https://github.com/Effect-TS/effect/pull/8796) [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2) Thanks @fubhy! - Fix `Channel.acquireUseRelease` leaking resources when interrupted during acquisition. + +- [#8813](https://github.com/Effect-TS/effect/pull/8813) [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81) Thanks @fubhy! - Fix Floyd-Warshall path reconstruction looping on zero-cost cycles after floating-point rounding. + +- [#8781](https://github.com/Effect-TS/effect/pull/8781) [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e) Thanks @fubhy! - Fix `Layer.buildWithMemoMap` retries returning an already-finalized resource by deferring each build until execution. + +- [#8807](https://github.com/Effect-TS/effect/pull/8807) [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304) Thanks @fubhy! - Fix `Logger.batched` shutdown to finish in-flight output before flushing remaining entries, without overlapping flushes. + +- [#8788](https://github.com/Effect-TS/effect/pull/8788) [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1) Thanks @fubhy! - Fix Pool shutdown to wait for borrowed resources to be finalized before releasing owner scope dependencies. + +- [#8786](https://github.com/Effect-TS/effect/pull/8786) [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562) Thanks @fubhy! - Fix PubSub publishers accepting surplus messages after `end` when they yield during publishing. + +- [#8790](https://github.com/Effect-TS/effect/pull/8790) [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb) Thanks @fubhy! - Reject NaN and fractional PubSub capacities to prevent nonterminating batch publication, while preserving Infinity support. + +- [#8787](https://github.com/Effect-TS/effect/pull/8787) [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e) Thanks @fubhy! - Fix PubSub take and takeAll delivering the final message before data published during waiter registration. + +- [#8783](https://github.com/Effect-TS/effect/pull/8783) [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f) Thanks @fubhy! - Fix `ManagedRuntime` disposal deadlocking when called from one of its own fibers, including their child fibers. + +- [#8803](https://github.com/Effect-TS/effect/pull/8803) [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6) Thanks @fubhy! - Report ScopedCache finalizer failures during expiry and capacity eviction without failing reads or blocking replacement lookups. + +- [#8793](https://github.com/Effect-TS/effect/pull/8793) [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15) Thanks @fubhy! - Fix `Stream.tapSink` hanging when the sink finishes during an upstream pull. + +- [#8797](https://github.com/Effect-TS/effect/pull/8797) [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75) Thanks @fubhy! - Fix `Sink.make` to return successful pipeline results instead of failing with a channel completion signal. + +- [#8806](https://github.com/Effect-TS/effect/pull/8806) [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3) Thanks @fubhy! - Preserve one-shot iterable inputs across transaction retries in TxPriorityQueue.offerAll, TxHashMap.fromIterable/setMany/removeMany, and TxHashSet.fromIterable. + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/effect/CONFIG.md b/packages/effect/CONFIG.md similarity index 100% rename from repos/effect/packages/effect/CONFIG.md rename to packages/effect/CONFIG.md diff --git a/repos/effect/packages/effect/HTTPAPI.md b/packages/effect/HTTPAPI.md similarity index 100% rename from repos/effect/packages/effect/HTTPAPI.md rename to packages/effect/HTTPAPI.md diff --git a/repos/effect/packages/effect/LICENSE b/packages/effect/LICENSE similarity index 100% rename from repos/effect/packages/effect/LICENSE rename to packages/effect/LICENSE diff --git a/repos/effect/packages/effect/MCP.md b/packages/effect/MCP.md similarity index 100% rename from repos/effect/packages/effect/MCP.md rename to packages/effect/MCP.md diff --git a/repos/effect/packages/effect/OPTIC.md b/packages/effect/OPTIC.md similarity index 100% rename from repos/effect/packages/effect/OPTIC.md rename to packages/effect/OPTIC.md diff --git a/repos/effect/packages/effect/README.md b/packages/effect/README.md similarity index 100% rename from repos/effect/packages/effect/README.md rename to packages/effect/README.md diff --git a/repos/effect/packages/effect/SCHEMA.md b/packages/effect/SCHEMA.md similarity index 100% rename from repos/effect/packages/effect/SCHEMA.md rename to packages/effect/SCHEMA.md diff --git a/repos/effect/packages/effect/benchmark/Pool.ts b/packages/effect/benchmark/Pool.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/Pool.ts rename to packages/effect/benchmark/Pool.ts diff --git a/repos/effect/packages/effect/benchmark/http/multipart.ts b/packages/effect/benchmark/http/multipart.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/multipart.ts rename to packages/effect/benchmark/http/multipart.ts diff --git a/repos/effect/packages/effect/benchmark/http/serverAllocations.ts b/packages/effect/benchmark/http/serverAllocations.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/serverAllocations.ts rename to packages/effect/benchmark/http/serverAllocations.ts diff --git a/repos/effect/packages/effect/benchmark/http/tracer.ts b/packages/effect/benchmark/http/tracer.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/tracer.ts rename to packages/effect/benchmark/http/tracer.ts diff --git a/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts b/packages/effect/benchmark/httpapi/sseClientDecoder.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts rename to packages/effect/benchmark/httpapi/sseClientDecoder.ts diff --git a/repos/effect/packages/effect/benchmark/net/NetAddress.ts b/packages/effect/benchmark/net/NetAddress.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/net/NetAddress.ts rename to packages/effect/benchmark/net/NetAddress.ts diff --git a/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts b/packages/effect/benchmark/rpc/RpcSerialization.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts rename to packages/effect/benchmark/rpc/RpcSerialization.ts diff --git a/repos/effect/packages/effect/benchmark/schema/Optic.ts b/packages/effect/benchmark/schema/Optic.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/Optic.ts rename to packages/effect/benchmark/schema/Optic.ts diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.md b/packages/effect/benchmark/schema/SchemaBinary.md similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaBinary.md rename to packages/effect/benchmark/schema/SchemaBinary.md diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts b/packages/effect/benchmark/schema/SchemaBinary.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts rename to packages/effect/benchmark/schema/SchemaBinary.ts diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaError.ts b/packages/effect/benchmark/schema/SchemaError.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaError.ts rename to packages/effect/benchmark/schema/SchemaError.ts diff --git a/repos/effect/packages/effect/benchmark/stream/splitLines.ts b/packages/effect/benchmark/stream/splitLines.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/stream/splitLines.ts rename to packages/effect/benchmark/stream/splitLines.ts diff --git a/repos/effect/packages/effect/package.json b/packages/effect/package.json similarity index 97% rename from repos/effect/packages/effect/package.json rename to packages/effect/package.json index d042e717c..48e82a448 100644 --- a/repos/effect/packages/effect/package.json +++ b/packages/effect/package.json @@ -1,7 +1,7 @@ { "name": "effect", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "The missing standard library for TypeScript, for writing production-grade software.", "homepage": "https://effect.website", @@ -110,11 +110,11 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "ajv": "^8.20.0", "ajv-draft-04": "^1.0.0", "ast-types": "^0.16.3", - "immer": "^11.1.18", + "immer": "^11.1.21", "protobufjs": "^8.8.0", "tinybench": "^6.2.0", "valibot": "^1.5.0" diff --git a/repos/effect/packages/effect/runtimeperf/README.md b/packages/effect/runtimeperf/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/README.md rename to packages/effect/runtimeperf/README.md diff --git a/repos/effect/packages/effect/runtimeperf/compare.mts b/packages/effect/runtimeperf/compare.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/compare.mts rename to packages/effect/runtimeperf/compare.mts diff --git a/repos/effect/packages/effect/runtimeperf/config.json b/packages/effect/runtimeperf/config.json similarity index 100% rename from repos/effect/packages/effect/runtimeperf/config.json rename to packages/effect/runtimeperf/config.json diff --git a/repos/effect/packages/effect/runtimeperf/materialize.mts b/packages/effect/runtimeperf/materialize.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/materialize.mts rename to packages/effect/runtimeperf/materialize.mts diff --git a/repos/effect/packages/effect/runtimeperf/run.mts b/packages/effect/runtimeperf/run.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/run.mts rename to packages/effect/runtimeperf/run.mts diff --git a/repos/effect/packages/effect/runtimeperf/stats.mts b/packages/effect/runtimeperf/stats.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/stats.mts rename to packages/effect/runtimeperf/stats.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md b/packages/effect/runtimeperf/suites/compiler-rebuild/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md rename to packages/effect/runtimeperf/suites/compiler-rebuild/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/data.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts b/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts rename to packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md b/packages/effect/runtimeperf/suites/schema-checked-sources/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md rename to packages/effect/runtimeperf/suites/schema-checked-sources/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts b/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md b/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md rename to packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md b/packages/effect/runtimeperf/suites/schema-construction/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md rename to packages/effect/runtimeperf/suites/schema-construction/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md b/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md rename to packages/effect/runtimeperf/suites/schema-construction/RESULTS.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md b/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md rename to packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts b/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts rename to packages/effect/runtimeperf/suites/schema-construction/fixtures.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts b/packages/effect/runtimeperf/suites/schema-construction/profile.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts rename to packages/effect/runtimeperf/suites/schema-construction/profile.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts b/packages/effect/runtimeperf/suites/schema-construction/run.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts rename to packages/effect/runtimeperf/suites/schema-construction/run.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts b/packages/effect/runtimeperf/suites/schema-construction/worker.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts rename to packages/effect/runtimeperf/suites/schema-construction/worker.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts b/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts b/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts b/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts b/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/cold.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts b/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts diff --git a/repos/effect/packages/effect/runtimeperf/test/materialize.test.mts b/packages/effect/runtimeperf/test/materialize.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/materialize.test.mts rename to packages/effect/runtimeperf/test/materialize.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/registry.test.mts b/packages/effect/runtimeperf/test/registry.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/registry.test.mts rename to packages/effect/runtimeperf/test/registry.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts b/packages/effect/runtimeperf/test/schema-construction.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts rename to packages/effect/runtimeperf/test/schema-construction.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/stats.test.mts b/packages/effect/runtimeperf/test/stats.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/stats.test.mts rename to packages/effect/runtimeperf/test/stats.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/worker.test.mts b/packages/effect/runtimeperf/test/worker.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/worker.test.mts rename to packages/effect/runtimeperf/test/worker.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts b/packages/effect/runtimeperf/test/worktree-diff.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts rename to packages/effect/runtimeperf/test/worktree-diff.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/utils.mts b/packages/effect/runtimeperf/utils.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/utils.mts rename to packages/effect/runtimeperf/utils.mts diff --git a/repos/effect/packages/effect/runtimeperf/worker.mts b/packages/effect/runtimeperf/worker.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/worker.mts rename to packages/effect/runtimeperf/worker.mts diff --git a/repos/effect/packages/effect/runtimeperf/worktree-diff.mts b/packages/effect/runtimeperf/worktree-diff.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/worktree-diff.mts rename to packages/effect/runtimeperf/worktree-diff.mts diff --git a/repos/effect/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts similarity index 100% rename from repos/effect/packages/effect/src/Arbitrary.ts rename to packages/effect/src/Arbitrary.ts diff --git a/repos/effect/packages/effect/src/Array.ts b/packages/effect/src/Array.ts similarity index 97% rename from repos/effect/packages/effect/src/Array.ts rename to packages/effect/src/Array.ts index 65dd5016d..402966e7f 100644 --- a/repos/effect/packages/effect/src/Array.ts +++ b/packages/effect/src/Array.ts @@ -7,6 +7,7 @@ * arrays and preserve non-empty array types when the result is guaranteed to * contain values. * + * @stability stable * @since 2.0.0 */ import * as Equal from "./Equal.ts" @@ -45,6 +46,7 @@ import type { NoInfer, TupleOf } from "./Types.ts" * Array.Array === globalThis.Array // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -53,6 +55,7 @@ export const Array = globalThis.Array /** * Type lambda for `ReadonlyArray`, used for higher-kinded type operations. * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -82,6 +85,7 @@ export interface ReadonlyArrayTypeLambda extends TypeLambda { * @see {@link NonEmptyArray} — mutable counterpart * @see {@link isReadonlyArrayNonEmpty} — narrow a `ReadonlyArray` to this type * + * @stability stable * @category models * @since 2.0.0 */ @@ -115,6 +119,7 @@ export type NonEmptyReadonlyArray = readonly [A, ...Array] * @see {@link NonEmptyReadonlyArray} — readonly counterpart * @see {@link isArrayNonEmpty} — narrow an `Array` to this type * + * @stability stable * @category models * @since 2.0.0 */ @@ -143,6 +148,7 @@ export type NonEmptyArray = [A, ...Array] * @see {@link of} — create a single-element array * @see {@link fromIterable} — create from any iterable * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -172,6 +178,7 @@ export const make = >( * * @see {@link makeBy} — create an array by computing each element * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -201,6 +208,7 @@ export const allocate = (n: number): Array => new Arra * @see {@link range} — create a range of integers * @see {@link replicate} — repeat a single value * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -238,6 +246,7 @@ export const makeBy: { * * @see {@link makeBy} — generate values from a function * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -267,6 +276,7 @@ export const range = (start: number, end: number): NonEmptyArray => * * @see {@link makeBy} — vary values based on index * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -299,6 +309,7 @@ export const replicate: { * @see {@link ensure} — wrap a single value or return an existing array * @see {@link copy} — create a shallow copy of an array * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -331,6 +342,7 @@ export const fromIterable = (collection: Iterable): Array => * @see {@link of} — always wrap in a single-element array * @see {@link fromIterable} — convert any iterable * + * @stability stable * @category constructors * @since 3.3.0 */ @@ -360,6 +372,7 @@ export const ensure = (self: ReadonlyArray | A): Array => Array.isArray * @see {@link Record.toEntries} the equivalent function from the Record module * @see {@link Record.fromEntries} to build a record from an array of tuples * + * @stability stable * @category converting * @since 2.0.0 */ @@ -383,6 +396,7 @@ export const fromRecord: (self: Readonly>) => * * @see {@link getSomes} — extract `Some` values from an array of Options * + * @stability stable * @category converting * @since 2.0.0 */ @@ -417,6 +431,7 @@ export const fromOption: (self: Option.Option) => Array = Option.toArra * @see {@link matchLeft} — destructures into head + tail * @see {@link matchRight} — destructures into init + last * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -472,6 +487,7 @@ export const match: { * @see {@link match} — receives the full non-empty array * @see {@link matchRight} — destructures into init + last * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -527,6 +543,7 @@ export const matchLeft: { * @see {@link match} — receives the full non-empty array * @see {@link matchLeft} — destructures into head + tail * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -574,6 +591,7 @@ export const matchRight: { * @see {@link append} — add to the end * @see {@link prependAll} — prepend multiple elements * + * @stability stable * @category combining * @since 2.0.0 */ @@ -604,6 +622,7 @@ export const prepend: { * @see {@link prepend} — add a single element to the front * @see {@link appendAll} — add elements to the end * + * @stability stable * @category combining * @since 2.0.0 */ @@ -638,6 +657,7 @@ export const prependAll: { * @see {@link prepend} — add to the front * @see {@link appendAll} — append multiple elements * + * @stability stable * @category combining * @since 2.0.0 */ @@ -669,6 +689,7 @@ export const append: { * @see {@link append} — add a single element to the end * @see {@link prependAll} — add elements to the front * + * @stability stable * @category combining * @since 2.0.0 */ @@ -708,6 +729,7 @@ export const appendAll: { * @see {@link scanRight} — right-to-left scan * @see {@link reduce} — fold without intermediate values * + * @stability stable * @category folding * @since 2.0.0 */ @@ -748,6 +770,7 @@ export const scan: { * @see {@link scan} — left-to-right scan * @see {@link reduceRight} — fold without intermediate values * + * @stability stable * @category folding * @since 2.0.0 */ @@ -788,6 +811,7 @@ export const scanRight: { * @see {@link isArrayEmpty} — check for an empty array * @see {@link isArrayNonEmpty} — check for a non-empty array * + * @stability stable * @category guards * @since 2.0.0 */ @@ -811,6 +835,7 @@ export const isArray: { * @see {@link isReadonlyArrayEmpty} — readonly variant * @see {@link isArrayNonEmpty} — opposite check * + * @stability stable * @category guards * @since 4.0.0 */ @@ -831,6 +856,7 @@ export const isArrayEmpty = (self: Array): self is [] => self.length === 0 * @see {@link isArrayEmpty} — mutable variant * @see {@link isReadonlyArrayNonEmpty} — opposite check * + * @stability stable * @category guards * @since 4.0.0 */ @@ -857,6 +883,7 @@ export const isReadonlyArrayEmpty: (self: ReadonlyArray) => self is readon * @see {@link isReadonlyArrayNonEmpty} — readonly variant * @see {@link isArrayEmpty} — opposite check * + * @stability stable * @category guards * @since 4.0.0 */ @@ -883,6 +910,7 @@ export const isArrayNonEmpty: (self: Array) => self is NonEmptyArray = * @see {@link isArrayNonEmpty} — mutable variant * @see {@link isReadonlyArrayEmpty} — opposite check * + * @stability stable * @category guards * @since 4.0.0 */ @@ -904,6 +932,7 @@ export const isReadonlyArrayNonEmpty: (self: ReadonlyArray) => self is Non * Array.length([1, 2, 3]) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -952,6 +981,7 @@ export function isOutOfBounds(i: number, as: ReadonlyArray): boolean { * @see {@link head} for reading the first element as an `Option` * @see {@link last} for reading the last element as an `Option` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -987,6 +1017,7 @@ export const get: { * * @see {@link get} — safe version returning `Option` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -1025,6 +1056,7 @@ export const getUnsafe: { * @see {@link headNonEmpty} for getting only the first element * @see {@link tailNonEmpty} for getting only the elements after the first * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1057,6 +1089,7 @@ export const unprepend = ( * @see {@link initNonEmpty} for getting only the elements before the last * @see {@link lastNonEmpty} for getting only the last element * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1084,6 +1117,7 @@ export const unappend = ( * @see {@link headNonEmpty} — direct access when array is known non-empty * @see {@link last} — get the last element * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1108,6 +1142,7 @@ export const head: (self: ReadonlyArray) => Option.Option = get(0) * * @see {@link head} — safe version for possibly-empty arrays * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1133,6 +1168,7 @@ export const headNonEmpty: (self: NonEmptyReadonlyArray) => A = getUnsafe( * @see {@link lastNonEmpty} — direct access when array is known non-empty * @see {@link head} — get the first element * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1158,6 +1194,7 @@ export const last = (self: ReadonlyArray): Option.Option => * * @see {@link last} — safe version for possibly-empty arrays * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1186,6 +1223,7 @@ export const lastNonEmpty = (self: NonEmptyReadonlyArray): A => self[self. * @see {@link tailNonEmpty} — when the array is known non-empty * @see {@link init} — all elements except the last * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1212,6 +1250,7 @@ export function tail(self: Iterable): Option.Option> { * @see {@link tail} — safe version for possibly-empty arrays * @see {@link initNonEmpty} — all elements except the last * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1241,6 +1280,7 @@ export const tailNonEmpty = (self: NonEmptyReadonlyArray): Array => sel * @see {@link initNonEmpty} — when the array is known non-empty * @see {@link tail} — all elements except the first * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1267,6 +1307,7 @@ export function init(self: Iterable): Option.Option> { * @see {@link init} — safe version for possibly-empty arrays * @see {@link tailNonEmpty} — all elements except the first * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1298,6 +1339,7 @@ const clampCount = (n: number, length: number): number => Math.min(Count.normali * @see {@link takeWhile} for keeping an initial prefix while a predicate holds * @see {@link drop} for removing elements from the start * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1332,6 +1374,7 @@ export const take: { * @see {@link take} — keep from the start * @see {@link dropRight} — remove from the end * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1370,6 +1413,7 @@ export const takeRight: { * @see {@link dropWhile} for removing the matching prefix and keeping the rest * @see {@link span} for splitting the matching prefix from the remaining elements * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1407,6 +1451,7 @@ export const takeWhile: { * * @see {@link takeWhile} for taking a prefix based on a boolean predicate * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1465,6 +1510,7 @@ const spanIndex = (self: Iterable, predicate: (a: A, i: number) => boolean * @see {@link dropWhile} for keeping only the elements after the matching prefix * @see {@link splitWhere} for splitting at the first element that satisfies a predicate * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1511,6 +1557,7 @@ export const span: { * @see {@link dropWhile} for removing a prefix based on a predicate instead of a fixed count * @see {@link take} for keeping a fixed number of elements from the start * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1544,6 +1591,7 @@ export const drop: { * @see {@link drop} — remove from the start * @see {@link takeRight} — keep from the end * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1577,6 +1625,7 @@ export const dropRight: { * @see {@link takeWhile} — keep the matching prefix instead * @see {@link drop} — drop a fixed count * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1611,6 +1660,7 @@ export const dropWhile: { * @see {@link dropWhile} for dropping a prefix with a simple boolean predicate * @see {@link takeWhileFilter} for keeping only the matching prefix * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1652,6 +1702,7 @@ export const dropWhileFilter: { * @see {@link findLastIndex} — search from the end * @see {@link findFirst} — get the element itself * + * @stability stable * @category searching * @since 2.0.0 */ @@ -1688,6 +1739,7 @@ export const findFirstIndex: { * @see {@link findFirstIndex} — search from the start * @see {@link findLast} — get the element itself * + * @stability stable * @category searching * @since 2.0.0 */ @@ -1731,6 +1783,7 @@ export const findLastIndex: { * @see {@link findFirstIndex} — get the index instead * @see {@link findFirstWithIndex} — get both element and index * + * @stability stable * @category searching * @since 2.0.0 */ @@ -1768,6 +1821,7 @@ export const findFirst: { * @see {@link findFirst} — get only the element * @see {@link findFirstIndex} — get only the index * + * @stability stable * @category searching * @since 3.17.0 */ @@ -1826,6 +1880,7 @@ export const findFirstWithIndex: { * @see {@link findFirst} — search from the start * @see {@link findLastIndex} — get the index instead * + * @stability stable * @category searching * @since 2.0.0 */ @@ -1883,6 +1938,7 @@ export const findLast: { * @see {@link replace} — replace an existing element * @see {@link modify} — transform an element at an index * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1922,6 +1978,7 @@ export const insertAt: { * @see {@link modify} — transform an element with a function * @see {@link insertAt} — insert without removing * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1968,6 +2025,7 @@ export const replace: { * @see {@link modifyHeadNonEmpty} — modify the first element * @see {@link modifyLastNonEmpty} — modify the last element * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2014,6 +2072,7 @@ export const modify: { * @see {@link insertAt} — insert an element * @see {@link filter} — remove elements by predicate * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2050,6 +2109,7 @@ export const remove: { * Array.reverse([1, 2, 3, 4]) // => [4, 3, 2, 1] * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2081,6 +2141,7 @@ export const reverse = >( * @see {@link sortWith} — sort by a mapping function * @see {@link sortBy} — sort by multiple orders * + * @stability stable * @category sorting * @since 2.0.0 */ @@ -2120,6 +2181,7 @@ export const sort: { * @see {@link sort} for sorting with an `Order` that compares the elements directly * @see {@link sortBy} for sorting with multiple `Order`s applied in sequence * + * @stability stable * @category sorting * @since 2.0.0 */ @@ -2175,6 +2237,7 @@ export const sortWith: { * @see {@link sort} — sort by a single `Order` * @see {@link sortWith} — sort by a derived key * + * @stability stable * @category sorting * @since 2.0.0 */ @@ -2216,6 +2279,7 @@ export const sortBy = >( * @see {@link zipWith} — zip with a combiner function * @see {@link unzip} — inverse operation * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2248,6 +2312,7 @@ export const zip: { * * @see {@link zip} — zip into tuples * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2283,6 +2348,7 @@ export const zipWith: { * * @see {@link zip} — combine two arrays into pairs * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2326,6 +2392,7 @@ export const unzip: >( * * @see {@link join} — intersperse and join into a string * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2370,6 +2437,7 @@ export const intersperse: { * @see {@link setHeadNonEmpty} — replace with a fixed value * @see {@link modifyLastNonEmpty} — modify the last element * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2403,6 +2471,7 @@ export const modifyHeadNonEmpty: { * @see {@link modifyHeadNonEmpty} — transform the head with a function * @see {@link setLastNonEmpty} — replace the last element * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2434,6 +2503,7 @@ export const setHeadNonEmpty: { * @see {@link setLastNonEmpty} — replace with a fixed value * @see {@link modifyHeadNonEmpty} — modify the first element * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2465,6 +2535,7 @@ export const modifyLastNonEmpty: { * @see {@link modifyLastNonEmpty} — transform the last element with a function * @see {@link setHeadNonEmpty} — replace the first element * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2502,6 +2573,7 @@ export const setLastNonEmpty: { * @see {@link take} for taking a fixed number of elements from the start * @see {@link drop} for dropping a fixed number of elements from the start * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2547,6 +2619,7 @@ export const rotate: { * * @see {@link contains} for the `Equal.equivalence()` variant * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2582,6 +2655,7 @@ export const containsWith = (isEquivalent: (self: A, that: A) => boolean): { * * @see {@link containsWith} — use custom equality * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2615,6 +2689,7 @@ export const contains: { * @see {@link chunksOf} — split into fixed-size chunks * @see {@link splitAt} — split at an index * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2672,6 +2747,7 @@ export const chop: { * @see {@link splitAtNonEmpty} — for non-empty arrays * @see {@link splitWhere} — split at a predicate boundary * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2713,6 +2789,7 @@ export const splitAt: { * * @see {@link splitAt} — for possibly-empty arrays * + * @stability stable * @category splitting * @since 4.0.0 */ @@ -2748,6 +2825,7 @@ export const splitAtNonEmpty: { * * @see {@link chunksOf} — split into fixed-size chunks * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2779,6 +2857,7 @@ export const split: { * @see {@link span} — splits at the first element that fails the predicate * @see {@link splitAt} — split at a fixed index * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2820,6 +2899,7 @@ export const splitWhere: { * * @see {@link fromIterable} — returns the same reference for arrays * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2852,6 +2932,7 @@ export const copy: { * @see {@link take} — truncate without padding * @see {@link replicate} — create an array of a single repeated value * + * @stability stable * @category transforming * @since 3.8.4 */ @@ -2901,6 +2982,7 @@ export const pad: { * @see {@link split} — split into a given number of groups * @see {@link window} — sliding windows * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2946,6 +3028,7 @@ export const chunksOf: { * * @see {@link chunksOf} — non-overlapping chunks * + * @stability stable * @category splitting * @since 3.13.2 */ @@ -2991,6 +3074,7 @@ export const window: { * @see {@link group} for grouping adjacent elements with `Equal.equivalence()` * @see {@link groupBy} for grouping all elements into a record by key, regardless of adjacency * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -3039,6 +3123,7 @@ export const groupWith: { * @see {@link groupWith} — use custom equality * @see {@link groupBy} — group by a key function into a record * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -3083,6 +3168,7 @@ export const group: (self: NonEmptyReadonlyArray) => NonEmptyArray { * @see {@link intersectionWith} for keeping elements present in both arrays * @see {@link differenceWith} for keeping elements present only in the first array * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3219,6 +3306,7 @@ export const unionWith: { * @see {@link intersection} — elements in both arrays * @see {@link difference} — elements only in the first array * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3268,6 +3356,7 @@ export const union: { * @see {@link unionWith} for keeping values from either array with custom equality * @see {@link differenceWith} for keeping values only from the first array with custom equality * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3306,6 +3395,7 @@ export const intersectionWith = (isEquivalent: (self: A, that: A) => boolean) * @see {@link union} — elements in either array * @see {@link difference} — elements only in the first array * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3343,6 +3433,7 @@ export const intersection: { * @see {@link unionWith} for keeping values from either array with custom equality * @see {@link intersectionWith} for keeping values present in both arrays with custom equality * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3381,6 +3472,7 @@ export const differenceWith = (isEquivalent: (self: A, that: A) => boolean): * @see {@link union} — elements in either array * @see {@link intersection} — elements in both arrays * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -3418,6 +3510,7 @@ export const difference: { * @see {@link of} — create a single-element array * @see {@link make} — create from multiple values * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -3437,6 +3530,7 @@ export const empty: () => Array = () => [] * @see {@link make} — create from multiple values * @see {@link empty} — create an empty array * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -3446,6 +3540,7 @@ export const of = (a: A): NonEmptyArray => [a] * Utility types for working with `ReadonlyArray` at the type level. Use these * to infer element types, preserve non-emptiness, and flatten nested arrays. * + * @stability stable * @since 2.0.0 */ export declare namespace ReadonlyArray { @@ -3583,6 +3678,7 @@ export declare namespace ReadonlyArray { * * @see {@link flatMap} — map and flatten * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -3617,6 +3713,7 @@ export const map: { * @see {@link map} — transform without flattening * @see {@link flatten} — flatten without mapping * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -3661,6 +3758,7 @@ export const flatMap: { * * @see {@link flatMap} — map then flatten in one step * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -3686,6 +3784,7 @@ export const flatten: >>(self: * @see {@link fromOption} — convert a single Option * @see {@link getSuccesses} — extract successes from Results * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -3722,6 +3821,7 @@ export const getSomes: >, X = any>( * @see {@link getSuccesses} — extract success values * @see {@link separate} — split into successes and failures * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3758,6 +3858,7 @@ export const getFailures = >>( * @see {@link getFailures} — extract failure values * @see {@link separate} — split into successes and failures * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3797,6 +3898,7 @@ export const getSuccesses = >>( * @see {@link filter} — keep original elements matching a predicate * @see {@link partition} for keeping both successes and failures * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -3839,6 +3941,7 @@ export const filterMap: { * @see {@link partition} — split into matching and non-matching * @see {@link filterMap} for transforming while filtering * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -3887,6 +3990,7 @@ export const filter: { * @see {@link filterMap} for discarding failures * @see {@link separate} — split an iterable of `Result` values * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -3943,6 +4047,7 @@ export const partition: { * @see {@link getSuccesses} — extract only successes * @see {@link partition} for computing `Result` values while splitting * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -3975,6 +4080,7 @@ export const separate: >>( * @see {@link reduceRight} — fold from right to left * @see {@link scan} — fold keeping intermediate values * + * @stability stable * @category folding * @since 2.0.0 */ @@ -4009,6 +4115,7 @@ export const reduce: { * @see {@link reduce} — fold from left to right * @see {@link scanRight} — fold keeping intermediate values * + * @stability stable * @category folding * @since 2.0.0 */ @@ -4038,6 +4145,7 @@ export const reduceRight: { * * @see {@link liftOption} — lift an Option-returning function * + * @stability stable * @category lifting * @since 2.0.0 */ @@ -4072,6 +4180,7 @@ export const liftPredicate: { // Note: I intentionally avoid using the NoInfer p * @see {@link liftPredicate} — lift a boolean predicate * @see {@link liftResult} — lift a Result-returning function * + * @stability stable * @category lifting * @since 2.0.0 */ @@ -4101,6 +4210,7 @@ export const liftOption = , B>( * @see {@link liftNullishOr} — lift a nullable-returning function * @see {@link fromOption} — convert from Option * + * @stability stable * @category converting * @since 4.0.0 */ @@ -4127,6 +4237,7 @@ export const fromNullishOr = (a: A): Array> => a == null ? emp * @see {@link fromNullishOr} — convert a single nullable value * @see {@link liftOption} — lift an Option-returning function * + * @stability stable * @category lifting * @since 4.0.0 */ @@ -4155,6 +4266,7 @@ export const liftNullishOr = , B>( * @see {@link flatMap} for mapping each element to an array and flattening * @see {@link fromNullishOr} for converting a single nullable value to an array * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -4194,6 +4306,7 @@ export const flatMapNullishOr: { * @see {@link liftOption} — lift an Option-returning function * @see {@link liftPredicate} — lift a boolean predicate * + * @stability stable * @category lifting * @since 4.0.0 */ @@ -4225,6 +4338,7 @@ export const liftResult = , E, B>( * * @see {@link some} — test if any element matches * + * @stability stable * @category guards * @since 2.0.0 */ @@ -4257,6 +4371,7 @@ export const every: { * @see {@link every} — test if all elements match * @see {@link contains} — test for a specific value * + * @stability stable * @category guards * @since 2.0.0 */ @@ -4294,6 +4409,7 @@ export const some: { * * @see {@link scan} for keeping intermediate accumulator values during a fold * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -4320,6 +4436,7 @@ export const extend: { * @see {@link max} — find the maximum * @see {@link sort} — sort the entire array * + * @stability stable * @category getters * @since 2.0.0 */ @@ -4343,6 +4460,7 @@ export const min: { * @see {@link min} — find the minimum * @see {@link sort} — sort the entire array * + * @stability stable * @category getters * @since 2.0.0 */ @@ -4367,6 +4485,7 @@ export const max: { * @see {@link makeBy} — generate from index * @see {@link range} — generate a numeric range * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -4402,6 +4521,7 @@ export const unfold = (b: B, f: (b: B) => Option.Option): * * @see {@link makeEquivalence} — create an equivalence for arrays * + * @stability stable * @category instances * @since 4.0.0 */ @@ -4424,6 +4544,7 @@ export const makeOrder: (O: Order.Order) => Order.Order> * * @see {@link makeOrder} — create an ordering for arrays * + * @stability stable * @category instances * @since 4.0.0 */ @@ -4452,6 +4573,7 @@ export const makeEquivalence: ( * * @see {@link map} for transforming each element into a new array * + * @stability stable * @category traversing * @since 2.0.0 */ @@ -4480,6 +4602,7 @@ export const forEach: { * @see {@link dedupe} — uses default equality * @see {@link dedupeAdjacentWith} — only dedupes consecutive elements * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -4523,6 +4646,7 @@ export const dedupeWith: { * @see {@link dedupeWith} — use custom equality * @see {@link dedupeAdjacent} — only dedupes consecutive elements * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -4567,6 +4691,7 @@ export const dedupe = >( * @see {@link dedupeAdjacent} — uses default equality * @see {@link dedupeWith} — dedupes all duplicates, not just adjacent * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -4604,6 +4729,7 @@ export const dedupeAdjacentWith: { * @see {@link dedupeAdjacentWith} — use custom equality * @see {@link dedupe} — remove all duplicates * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -4622,6 +4748,7 @@ export const dedupeAdjacent: (self: Iterable) => Array = dedupeAdjacent * * @see {@link intersperse} — insert separator elements without joining * + * @stability stable * @category folding * @since 2.0.0 */ @@ -4656,6 +4783,7 @@ export const join: { * @see {@link scan} — when you only need the accumulated results (not the final state) * @see {@link reduce} — when you only need the final accumulated value * + * @stability stable * @category folding * @since 2.0.0 */ @@ -4709,6 +4837,7 @@ export const mapAccum: { * * @see {@link cartesian} for returning tuples instead of applying a combiner * + * @stability stable * @category combining * @since 2.0.0 */ @@ -4743,6 +4872,7 @@ export const cartesianWith: { * * @see {@link cartesianWith} — apply a combiner to each pair * + * @stability stable * @category combining * @since 2.0.0 */ @@ -4790,6 +4920,7 @@ export const cartesian: { * @see {@link bindTo} — start a pipeline by naming the first array * @see {@link let_ let} — introduce a plain computed value * + * @stability stable * @category constructors * @since 3.2.0 */ @@ -4825,6 +4956,7 @@ export const Do: ReadonlyArray<{}> = of({}) * @see {@link bindTo} — name the first array in a pipeline * @see {@link let_ let} — add a plain computed value * + * @stability stable * @category sequencing * @since 3.2.0 */ @@ -4866,6 +4998,7 @@ export const bind: { * @see {@link Do} — start with an empty scope * @see {@link bind} — add another array variable to the scope * + * @stability stable * @category mapping * @since 3.2.0 */ @@ -4916,24 +5049,34 @@ export { * @see {@link Do} — start a do-notation pipeline * @see {@link bind} — introduce an array variable (produces cartesian product) * + * @stability stable * @category mapping * @since 3.2.0 */ let_ as let } -const reducer = Reducer.make((a, b) => a.concat(b), [] as any) +const combineConcat = (a: ReadonlyArray, b: ReadonlyArray): Array => a.concat(b) + +const combineAllConcat = (collection: Iterable>): Array => { + let out: Array = [] + for (const value of collection) { + out = out.concat(value) + } + return out +} /** * Returns a `Reducer` that combines `ReadonlyArray` values by concatenation. * * @see {@link makeReducerConcat} — mutable `Array` variant * + * @stability stable * @category folding * @since 4.0.0 */ export function getReadonlyReducerConcat(): Reducer.Reducer> { - return reducer + return Reducer.make>(combineConcat, [], combineAllConcat) } /** @@ -4941,11 +5084,12 @@ export function getReadonlyReducerConcat(): Reducer.Reducer> * * @see {@link getReadonlyReducerConcat} — readonly variant * + * @stability stable * @category folding * @since 4.0.0 */ export function makeReducerConcat(): Reducer.Reducer> { - return reducer + return Reducer.make>(combineConcat, [], combineAllConcat) } /** @@ -4971,6 +5115,7 @@ export function makeReducerConcat(): Reducer.Reducer> { * * @see {@link filter} — when you need the matching elements, not just the count * + * @stability stable * @category folding * @since 3.16.0 */ diff --git a/repos/effect/packages/effect/src/BigDecimal.ts b/packages/effect/src/BigDecimal.ts similarity index 98% rename from repos/effect/packages/effect/src/BigDecimal.ts rename to packages/effect/src/BigDecimal.ts index 729fcdae8..ede74868d 100644 --- a/repos/effect/packages/effect/src/BigDecimal.ts +++ b/packages/effect/src/BigDecimal.ts @@ -5,6 +5,7 @@ * divide, round, and format decimal values such as money, quantities, and * measurements. * + * @stability stable * @since 2.0.0 */ @@ -43,6 +44,7 @@ const TypeId = "~effect/BigDecimal" * d.scale // => 2 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -100,6 +102,7 @@ const BigDecimalProto: Omit = { * BigDecimal.isBigDecimal("123.45") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -133,6 +136,7 @@ export const isBigDecimal = (u: unknown): u is BigDecimal => hasProperty(u, Type * * @see {@link fromBigInt} for constructing an integer decimal from a `bigint` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -197,6 +201,7 @@ const one = makeNormalizedUnsafe(bigint1, 0) * * @see {@link format} for rendering normalized decimals as strings * + * @stability stable * @category scaling * @since 2.0.0 */ @@ -246,6 +251,7 @@ export const normalize = (self: BigDecimal): BigDecimal => { * * @see {@link round} for changing scale with configurable rounding * + * @stability stable * @category scaling * @since 2.0.0 */ @@ -285,6 +291,7 @@ export const scale: { * * @see {@link sumAll} for summing an iterable of `BigDecimal` values * + * @stability stable * @category math * @since 2.0.0 */ @@ -333,6 +340,7 @@ export const sum: { * * @see {@link sum} for adding two `BigDecimal` values * + * @stability stable * @category math * @since 3.16.0 */ @@ -364,6 +372,7 @@ export const sumAll = (collection: Iterable): BigDecimal => { * * @see {@link multiplyAll} for multiplying an iterable of `BigDecimal` values * + * @stability stable * @category math * @since 2.0.0 */ @@ -399,6 +408,7 @@ export const multiply: { * * @see {@link multiply} for multiplying two `BigDecimal` values * + * @stability stable * @category math * @since 4.0.0 */ @@ -431,6 +441,7 @@ export const multiplyAll = (collection: Iterable): BigDecimal => { * ) // => BigDecimal.fromBigInt(-1n) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -554,6 +565,7 @@ export const roundTerminal = (n: bigint): bigint => { * @see {@link divideUnsafe} for division that throws when the divisor is zero * @see {@link remainder} for the decimal remainder operation * + * @stability stable * @category math * @since 2.0.0 */ @@ -605,6 +617,7 @@ export const divide: { * * @see {@link divide} for division that returns `Option.none` when the divisor is zero * + * @stability stable * @category math * @since 4.0.0 */ @@ -684,6 +697,7 @@ const compare = (self: BigDecimal, that: BigDecimal): Ordering => { * BigDecimal.Order(a, c) // => 0 * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -710,6 +724,7 @@ export const Order: order.Order = order.make(compare) * BigDecimal.isLessThan(four, three) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -739,6 +754,7 @@ export const isLessThan: { * BigDecimal.isLessThanOrEqualTo(four, three) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -768,6 +784,7 @@ export const isLessThanOrEqualTo: { * BigDecimal.isGreaterThan(four, three) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -797,6 +814,7 @@ export const isGreaterThan: { * BigDecimal.isGreaterThanOrEqualTo(four, three) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -829,6 +847,7 @@ export const isGreaterThanOrEqualTo: { * * @see {@link clamp} for forcing a `BigDecimal` into an inclusive range * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -873,6 +892,7 @@ export const between: { * * @see {@link between} for checking whether a `BigDecimal` is already inside a range * + * @stability stable * @category math * @since 2.0.0 */ @@ -907,6 +927,7 @@ export const clamp: { * * @see {@link max} for selecting the larger value * + * @stability stable * @category math * @since 2.0.0 */ @@ -935,6 +956,7 @@ export const min: { * * @see {@link min} for selecting the smaller value * + * @stability stable * @category math * @since 2.0.0 */ @@ -960,6 +982,7 @@ export const max: { * BigDecimal.sign(BigDecimal.fromStringUnsafe("5")) // => 1 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -982,6 +1005,7 @@ export const sign = (n: BigDecimal): Ordering => n.value === bigint0 ? 0 : n.val * BigDecimal.abs(BigDecimal.fromStringUnsafe("5")) // => BigDecimal.fromBigInt(5n) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -1003,6 +1027,7 @@ export const abs = (n: BigDecimal): BigDecimal => n.value < bigint0 ? make(-n.va * BigDecimal.negate(BigDecimal.fromStringUnsafe("-6")) // => BigDecimal.fromBigInt(6n) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -1037,6 +1062,7 @@ export const negate = (n: BigDecimal): BigDecimal => make(-n.value, n.scale) * @see {@link remainderUnsafe} for remainder calculation that throws when the divisor is zero * @see {@link divide} for decimal quotient calculation * + * @stability stable * @category math * @since 2.0.0 */ @@ -1078,6 +1104,7 @@ export const remainder: { * * @see {@link remainder} for returning `Option.none` when the divisor is zero * + * @stability stable * @category math * @since 4.0.0 */ @@ -1114,6 +1141,7 @@ export const remainderUnsafe: { * BigDecimal.Equivalence(a, c) // => false * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -1141,6 +1169,7 @@ export const Equivalence: Equ.Equivalence = Equ.make((self, that) => * * @see {@link Equivalence} for passing decimal equality to APIs that require an `Equivalence` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1170,6 +1199,7 @@ export const equals: { * * @see {@link make} for constructing a decimal with an explicit scale * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1200,6 +1230,7 @@ export const fromBigInt = (n: bigint): BigDecimal => make(n, 0) * * @see {@link fromNumber} for returning `Option.none` when the number is not finite * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1236,6 +1267,7 @@ export const fromNumberUnsafe = (n: number): BigDecimal => { * @see {@link fromNumberUnsafe} for throwing when the number is not finite * @see {@link fromString} for parsing decimal strings directly * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1278,6 +1310,7 @@ export const fromNumber = (n: number): Option.Option => { * @see {@link fromStringUnsafe} for parsing that throws on invalid input * @see {@link fromNumber} for converting finite JavaScript numbers * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1350,6 +1383,7 @@ export const fromString = (s: string): Option.Option => { * * @see {@link fromString} for returning `Option.none` on invalid input * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1382,6 +1416,7 @@ export const fromStringUnsafe = (s: string): BigDecimal => { * * @see {@link toExponential} for always rendering scientific notation * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1418,6 +1453,7 @@ export const format = (n: BigDecimal): string => { * * @see {@link format} for plain decimal formatting when possible * + * @stability stable * @category converting * @since 3.11.0 */ @@ -1459,6 +1495,7 @@ export const toExponential = (n: BigDecimal): string => { * * @see {@link format} for preserving decimal precision as text * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1481,6 +1518,7 @@ export const toNumberUnsafe = (n: BigDecimal): number => Number(format(n)) * BigDecimal.isInteger(BigDecimal.fromStringUnsafe("1.1")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1502,6 +1540,7 @@ export const isInteger = (n: BigDecimal): boolean => normalize(n).scale <= 0 * BigDecimal.isZero(BigDecimal.fromStringUnsafe("1")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1524,6 +1563,7 @@ export const isZero = (n: BigDecimal): boolean => n.value === bigint0 * BigDecimal.isNegative(BigDecimal.fromStringUnsafe("1")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1546,6 +1586,7 @@ export const isNegative = (n: BigDecimal): boolean => n.value < bigint0 * BigDecimal.isPositive(BigDecimal.fromStringUnsafe("1")) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1579,6 +1620,7 @@ const isBigDecimalArgs = (args: IArguments) => isBigDecimal(args[0]) * @see {@link floor} for fixed rounding toward negative infinity * @see {@link truncate} for fixed rounding toward zero * + * @stability stable * @category math * @since 3.16.0 */ @@ -1617,6 +1659,7 @@ export type RoundingMode = * @see {@link floor} for fixed rounding toward negative infinity * @see {@link truncate} for fixed rounding toward zero * + * @stability stable * @category math * @since 3.16.0 */ @@ -1692,6 +1735,7 @@ export const round: { * @see {@link ceil} for rounding toward positive infinity * @see {@link floor} for rounding toward negative infinity * + * @stability stable * @category math * @since 3.16.0 */ @@ -1733,6 +1777,7 @@ export const truncate: { * BigDecimal.ceil(BigDecimal.fromStringUnsafe("-14.5")) // => BigDecimal.fromBigInt(-14n) * ``` * + * @stability stable * @category math * @since 3.16.0 */ @@ -1788,6 +1833,7 @@ export const digitAt: { * @see {@link truncate} for rounding toward zero * @see {@link round} for configurable rounding modes * + * @stability stable * @category math * @since 3.16.0 */ diff --git a/repos/effect/packages/effect/src/BigInt.ts b/packages/effect/src/BigInt.ts similarity index 97% rename from repos/effect/packages/effect/src/BigInt.ts rename to packages/effect/src/BigInt.ts index 18ac6ba68..99ab0b4b4 100644 --- a/repos/effect/packages/effect/src/BigInt.ts +++ b/packages/effect/src/BigInt.ts @@ -6,6 +6,7 @@ * that return `Option`, integer square roots, aggregation, ordering, * equivalence, reducers, and combiners. * + * @stability stable * @since 2.0.0 */ @@ -43,6 +44,7 @@ import * as Reducer from "./Reducer.ts" * BigInt.BigInt("456") // => 456n * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -68,6 +70,7 @@ const bigint2 = BigInt(2) * BigInt.isBigInt(1) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -91,6 +94,7 @@ export const isBigInt: (u: unknown) => u is bigint = predicate.isBigInt * * @see {@link sumAll} for summing an iterable of `bigint` values * + * @stability stable * @category math * @since 2.0.0 */ @@ -116,6 +120,7 @@ export const sum: { * * @see {@link multiplyAll} for multiplying an iterable of `bigint` values * + * @stability stable * @category math * @since 2.0.0 */ @@ -139,6 +144,7 @@ export const multiply: { * BigInt.subtract(2n, 3n) // => -1n * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -172,6 +178,7 @@ export const subtract: { * @see {@link divideUnsafe} for division that throws when the divisor is `0n` * @see {@link remainder} for the JavaScript remainder operation * + * @stability stable * @category math * @since 2.0.0 */ @@ -211,6 +218,7 @@ export const divide: { * * @see {@link divide} for division that returns `Option.none` when the divisor is `0n` * + * @stability stable * @category math * @since 4.0.0 */ @@ -234,6 +242,7 @@ export const divideUnsafe: { * BigInt.increment(2n) // => 3n * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -254,6 +263,7 @@ export const increment = (n: bigint): bigint => n + bigint1 * BigInt.decrement(3n) // => 2n * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -281,6 +291,7 @@ export const decrement = (n: bigint): bigint => n - bigint1 * BigInt.Order(a, c) // => 0 * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -303,6 +314,7 @@ export const Order: order.Order = order.BigInt * BigInt.Equivalence(1n, 2n) // => false * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -325,6 +337,7 @@ export const Equivalence: Equ.Equivalence = Equ.BigInt * BigInt.isLessThan(4n, 3n) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -350,6 +363,7 @@ export const isLessThan: { * BigInt.isLessThanOrEqualTo(4n, 3n) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -375,6 +389,7 @@ export const isLessThanOrEqualTo: { * BigInt.isGreaterThan(4n, 3n) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -400,6 +415,7 @@ export const isGreaterThan: { * BigInt.isGreaterThanOrEqualTo(4n, 3n) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -429,6 +445,7 @@ export const isGreaterThanOrEqualTo: { * * @see {@link clamp} for forcing a `bigint` into an inclusive range * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -470,6 +487,7 @@ export const between: { * * @see {@link between} for checking whether a `bigint` is already inside a range * + * @stability stable * @category math * @since 2.0.0 */ @@ -501,6 +519,7 @@ export const clamp: { * * @see {@link max} for selecting the larger value * + * @stability stable * @category math * @since 2.0.0 */ @@ -526,6 +545,7 @@ export const min: { * * @see {@link min} for selecting the smaller value * + * @stability stable * @category math * @since 2.0.0 */ @@ -551,6 +571,7 @@ export const max: { * BigInt.sign(5n) // => 1 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -573,6 +594,7 @@ export const sign = (n: bigint): Ordering => order.BigInt(n, bigint0) * BigInt.abs(5n) // => 5n * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -597,6 +619,7 @@ export const abs = (n: bigint): bigint => (n < bigint0 ? -n : n) * * @see {@link lcm} for computing the least common multiple * + * @stability stable * @category math * @since 2.0.0 */ @@ -631,6 +654,7 @@ export const gcd: { * * @see {@link gcd} for computing the greatest common divisor * + * @stability stable * @category math * @since 2.0.0 */ @@ -673,6 +697,7 @@ export const lcm: { * * @see {@link sqrt} for returning `Option.none` when the input is negative * + * @stability stable * @category math * @since 4.0.0 */ @@ -717,6 +742,7 @@ export const sqrtUnsafe = (n: bigint): bigint => { * * @see {@link sqrtUnsafe} for square root computation that throws on negative input * + * @stability stable * @category math * @since 2.0.0 */ @@ -742,6 +768,7 @@ export const sqrt = (n: bigint): Option.Option => * @see {@link sum} for adding two `bigint` values * @see {@link ReducerSum} for summing through APIs that consume a `Reducer` * + * @stability stable * @category math * @since 2.0.0 */ @@ -771,6 +798,7 @@ export const sumAll = (collection: Iterable): bigint => { * @see {@link multiply} for multiplying two `bigint` values * @see {@link ReducerMultiply} for multiplying through APIs that consume a `Reducer` * + * @stability stable * @category math * @since 2.0.0 */ @@ -810,6 +838,7 @@ export const multiplyAll = (collection: Iterable): bigint => { * * @see {@link fromNumber} for converting a safe integer number to `bigint` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -844,6 +873,7 @@ export const toNumber = (b: bigint): Option.Option => { * * @see {@link BigInt} for native constructor coercion that throws on invalid input * + * @stability stable * @category converting * @since 2.4.12 */ @@ -883,6 +913,7 @@ export const fromString = (s: string): Option.Option => { * @see {@link toNumber} for converting `bigint` values back to safe integer numbers * @see {@link BigInt} for native constructor coercion * + * @stability stable * @category converting * @since 2.4.12 */ @@ -921,6 +952,7 @@ export function fromNumber(n: number): Option.Option { * * @see {@link divide} for quotient calculation with division-by-zero represented as `Option.none` * + * @stability stable * @category math * @since 4.0.0 */ @@ -943,6 +975,7 @@ export const remainder: { * @see {@link sumAll} for summing an iterable directly * @see {@link ReducerMultiply} for multiplying `bigint` values * + * @stability stable * @category math * @since 4.0.0 */ @@ -962,6 +995,7 @@ export const ReducerSum: Reducer.Reducer = Reducer.make((a, b) => a + b, * @see {@link multiplyAll} for multiplying an iterable directly * @see {@link ReducerSum} for summing `bigint` values * + * @stability stable * @category math * @since 4.0.0 */ @@ -984,6 +1018,7 @@ export const ReducerMultiply: Reducer.Reducer = Reducer.make((a, b) => a * @see {@link CombinerMin} for keeping the smallest `bigint` * @see {@link max} for comparing two `bigint` values directly * + * @stability stable * @category math * @since 4.0.0 */ @@ -999,6 +1034,7 @@ export const CombinerMax: Combiner.Combiner = Combiner.max(Order) * @see {@link CombinerMax} for keeping the largest `bigint` * @see {@link min} for comparing two `bigint` values directly * + * @stability stable * @category math * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Boolean.ts b/packages/effect/src/Boolean.ts similarity index 96% rename from repos/effect/packages/effect/src/Boolean.ts rename to packages/effect/src/Boolean.ts index 405e7b35a..dbcc3e5ad 100644 --- a/repos/effect/packages/effect/src/Boolean.ts +++ b/packages/effect/src/Boolean.ts @@ -6,6 +6,7 @@ * logical operations, checking collections with `every` or `some`, ordering * booleans, and reducing boolean values. * + * @stability stable * @since 2.0.0 */ import * as Equ from "./Equivalence.ts" @@ -39,6 +40,7 @@ import * as Reducer from "./Reducer.ts" * Boolean.Boolean(0) // => false * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -60,6 +62,7 @@ export const Boolean = globalThis.Boolean * Boolean.isBoolean("true") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -83,6 +86,7 @@ export const isBoolean: (input: unknown) => input is boolean = predicate.isBoole * }) // => "It's true!" * ``` * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -119,6 +123,7 @@ export const match: { * Boolean.Order(true, true) // => 0 * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -141,6 +146,7 @@ export const Order: order.Order = order.Boolean * Boolean.Equivalence(true, false) // => false * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -162,6 +168,7 @@ export const Equivalence: Equ.Equivalence = Equ.Boolean * Boolean.not(false) // => true * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -189,6 +196,7 @@ export const not = (self: boolean): boolean => !self * Boolean.and(false, false) // => false * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -215,6 +223,7 @@ export const and: { * Boolean.nand(false, false) // => true * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -241,6 +250,7 @@ export const nand: { * Boolean.or(false, false) // => false * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -267,6 +277,7 @@ export const or: { * Boolean.nor(false, false) // => true * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -293,6 +304,7 @@ export const nor: { * Boolean.xor(false, false) // => false * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -319,6 +331,7 @@ export const xor: { * Boolean.eqv(false, false) // => true * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -345,6 +358,7 @@ export const eqv: { * Boolean.implies(false, false) // => true * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -372,6 +386,7 @@ export const implies: { * @see {@link some} for checking whether at least one value is `true` * @see {@link ReducerAnd} for reducing booleans with AND through a `Reducer` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -403,6 +418,7 @@ export const every = (collection: Iterable): boolean => { * @see {@link every} for checking whether all values are `true` * @see {@link ReducerOr} for reducing booleans with OR through a `Reducer` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -435,6 +451,7 @@ export const some = (collection: Iterable): boolean => { * @see {@link ReducerOr} for reducing with OR semantics * @see {@link every} for checking an iterable directly * + * @stability stable * @category math * @since 4.0.0 */ @@ -455,6 +472,7 @@ export const ReducerAnd: Reducer.Reducer = Reducer.make((a, b) => a && * @see {@link ReducerAnd} for reducing with AND semantics * @see {@link some} for checking an iterable directly * + * @stability stable * @category math * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Brand.ts b/packages/effect/src/Brand.ts similarity index 97% rename from repos/effect/packages/effect/src/Brand.ts rename to packages/effect/src/Brand.ts index 1cbba59fd..40b41a5ec 100644 --- a/repos/effect/packages/effect/src/Brand.ts +++ b/packages/effect/src/Brand.ts @@ -5,6 +5,7 @@ * information lives in the type system unless you choose a validating * constructor. * + * @stability stable * @since 2.0.0 */ import * as Arr from "./Array.ts" @@ -29,6 +30,7 @@ const TypeId = "~effect/Brand" * @see {@link Branded} for applying a brand key to a base type * @see {@link Constructor} for validating or constructing branded values * + * @stability stable * @category models * @since 2.0.0 */ @@ -53,6 +55,7 @@ export interface Brand { * @see {@link check} for creating a constructor from schema checks * @see {@link all} for combining brand constructors * + * @stability stable * @category models * @since 2.0.0 */ @@ -100,6 +103,7 @@ export interface Constructor> { * `BrandError` is an error-like model with `_tag`, `name`, `message`, and * `toString`; it does not extend JavaScript `Error`. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -147,6 +151,7 @@ export class BrandError { * Namespace containing type-level helpers for working with branded types and * brand constructors. * + * @stability stable * @since 2.0.0 */ export declare namespace Brand { @@ -205,6 +210,7 @@ export declare namespace Brand { /** * A type alias for creating branded types more concisely. * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -222,6 +228,7 @@ export type Branded = A & Brand * @see {@link make} for constructing branded values with validation. * @see {@link check} for constructing branded values from schema checks. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -243,6 +250,7 @@ export function nominal>(): Constructor { * * @see {@link nominal} for a brand constructor that performs no validation. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -268,6 +276,7 @@ export function make>( * * @see {@link nominal} for a brand constructor without runtime validation * @see {@link all} for combining multiple brand constructors + * @stability stable * @category constructors * @since 4.0.0 */ @@ -302,6 +311,7 @@ export function check>( * constructor succeeds only when all checks pass. If no runtime checks are * present, it behaves as a nominal constructor. * + * @stability stable * @category combining * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/ByteSize.ts b/packages/effect/src/ByteSize.ts similarity index 94% rename from repos/effect/packages/effect/src/ByteSize.ts rename to packages/effect/src/ByteSize.ts index ddacee0f7..3300883e4 100644 --- a/repos/effect/packages/effect/src/ByteSize.ts +++ b/packages/effect/src/ByteSize.ts @@ -3,6 +3,7 @@ * * Decimal units use powers of 1,000 and binary units use powers of 1,024. * + * @stability unstable * @since 4.0.0 */ import * as BI from "./BigInt.ts" @@ -24,6 +25,7 @@ const binaryBase = BigInt(1024) /** * Represents an exact, non-negative number of bytes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -40,6 +42,7 @@ export type ByteSize = Brand.Branded * * @see {@link fromString} for external strings and fractional quantities * @see {@link fromStringUnsafe} for throwing string validation + * @stability unstable * @category models * @since 4.0.0 */ @@ -75,6 +78,7 @@ type InputUnit = /** * Canonical decimal byte unit symbols. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -94,6 +98,7 @@ export type DecimalUnit = /** * Canonical binary byte unit symbols. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -111,6 +116,7 @@ export type BinaryUnit = /** * Canonical decimal and binary byte unit symbols. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -119,6 +125,7 @@ export type Unit = DecimalUnit | BinaryUnit /** * Options controlling compact byte-size formatting. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -179,6 +186,7 @@ const make = (value: bigint): ByteSize => value as ByteSize /** * The byte size containing zero bytes. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -218,6 +226,7 @@ const fromQuantity = (quantity: number | bigint, unit: UnitInfo): ByteSize => { * bigint arithmetic, including above the safe integer range. * * @see {@link fromString} for non-throwing validation + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -245,6 +254,7 @@ export const fromStringUnsafe = (input: string): ByteSize => { * as `fromStringUnsafe`. * * @see {@link fromStringUnsafe} for throwing validation + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -253,6 +263,7 @@ export const fromString: (input: string) => Option.Option = Option.lif /** * Decodes a trusted input into a byte size and throws for invalid input. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -272,6 +283,7 @@ export const fromInputUnsafe = (input: Input): ByteSize => { /** * Decodes an input into a byte size, returning `None` for invalid input. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -280,6 +292,7 @@ export const fromInput: (input: Input) => Option.Option = Option.liftT /** * Creates a byte size from a non-negative byte count. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -292,6 +305,7 @@ const unitConstructor = (symbol: Unit) => (value: number | bigint): ByteSize => /** * Creates a decimal kilobyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -299,6 +313,7 @@ export const kilobytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a decimal megabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -306,6 +321,7 @@ export const megabytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a decimal gigabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -313,6 +329,7 @@ export const gigabytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a decimal terabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -320,6 +337,7 @@ export const terabytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a decimal petabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -327,6 +345,7 @@ export const petabytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a decimal exabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -334,6 +353,7 @@ export const exabytes: (value: number | bigint) => ByteSize = unitConstructor("E /** * Creates a decimal zettabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -341,6 +361,7 @@ export const zettabytes: (value: number | bigint) => ByteSize = unitConstructor( /** * Creates a decimal yottabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -348,6 +369,7 @@ export const yottabytes: (value: number | bigint) => ByteSize = unitConstructor( /** * Creates a decimal ronnabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -355,6 +377,7 @@ export const ronnabytes: (value: number | bigint) => ByteSize = unitConstructor( /** * Creates a decimal quettabyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -362,6 +385,7 @@ export const quettabytes: (value: number | bigint) => ByteSize = unitConstructor /** * Creates a binary kibibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -369,6 +393,7 @@ export const kibibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary mebibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -376,6 +401,7 @@ export const mebibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary gibibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -383,6 +409,7 @@ export const gibibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary tebibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -390,6 +417,7 @@ export const tebibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary pebibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -397,6 +425,7 @@ export const pebibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary exbibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -404,6 +433,7 @@ export const exbibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary zebibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -411,6 +441,7 @@ export const zebibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Creates a binary yobibyte value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -419,6 +450,7 @@ export const yobibytes: (value: number | bigint) => ByteSize = unitConstructor(" /** * Checks whether a value is a byte size. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -427,6 +459,7 @@ export const isByteSize = (input: unknown): input is ByteSize => typeof input == /** * Checks whether a byte size is zero. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -435,6 +468,7 @@ export const isZero = (self: ByteSize): boolean => self === bigint0 /** * Returns the exact byte count as a bigint. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -443,6 +477,7 @@ export const toBigInt = (self: ByteSize): bigint => self /** * Converts a byte size to a safe integer, returning `None` when it is too large. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -451,6 +486,7 @@ export const toNumber: (self: ByteSize) => Option.Option = BI.toNumber /** * Converts a byte size to a safe integer and throws when it is too large. * + * @stability unstable * @category unsafe * @since 4.0.0 */ @@ -460,6 +496,7 @@ export const toNumberUnsafe = (self: ByteSize): number => /** * Converts a byte size to an approximate number of the specified unit. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -471,6 +508,7 @@ export const toUnit: { /** * Provides an order for byte sizes. * + * @stability unstable * @category instances * @since 4.0.0 */ @@ -479,6 +517,7 @@ export const Order: order.Order = BI.Order /** * Provides an equivalence for byte sizes. * + * @stability unstable * @category instances * @since 4.0.0 */ @@ -487,6 +526,7 @@ export const Equivalence: Equ.Equivalence = BI.Equivalence /** * Returns whether a byte size is in an inclusive range. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -498,6 +538,7 @@ export const between: { /** * Returns the smaller byte size. * + * @stability unstable * @category ordering * @since 4.0.0 */ @@ -509,6 +550,7 @@ export const min: { /** * Returns the larger byte size. * + * @stability unstable * @category ordering * @since 4.0.0 */ @@ -520,6 +562,7 @@ export const max: { /** * Constrains a byte size to an inclusive range. * + * @stability unstable * @category ordering * @since 4.0.0 */ @@ -531,6 +574,7 @@ export const clamp: { /** * Checks whether the first byte size is less than the second. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -542,6 +586,7 @@ export const isLessThan: { /** * Checks whether the first byte size is at most the second. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -553,6 +598,7 @@ export const isLessThanOrEqualTo: { /** * Checks whether the first byte size is greater than the second. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -564,6 +610,7 @@ export const isGreaterThan: { /** * Checks whether the first byte size is at least the second. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -575,6 +622,7 @@ export const isGreaterThanOrEqualTo: { /** * Checks whether two byte sizes contain the same count. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -586,6 +634,7 @@ export const equals: { /** * Adds two byte sizes exactly. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -597,6 +646,7 @@ export const sum: { /** * Subtracts byte sizes, returning `None` on underflow. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -611,6 +661,7 @@ export const subtract: { /** * Subtracts byte sizes and throws on underflow. * + * @stability unstable * @category unsafe * @since 4.0.0 */ @@ -630,6 +681,7 @@ const scalar = (input: number | bigint, positive: boolean): bigint | undefined = /** * Multiplies a byte size by a non-negative integer scalar. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -644,6 +696,7 @@ export const times: { /** * Divides a byte size by a positive integer, discarding any remainder. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -675,6 +728,7 @@ const formatWithUnit = (value: bigint, unit: UnitInfo, precision: number, traili /** * Formats a byte size with canonical decimal or binary unit symbols. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -700,6 +754,7 @@ export const format = (self: ByteSize, options: FormatOptions = {}): string => { /** * Reducer that sums byte sizes from zero. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -708,6 +763,7 @@ export const ReducerSum: Reducer.Reducer = BI.ReducerSum as any /** * Combiner that keeps the largest byte size. * + * @stability unstable * @category math * @since 4.0.0 */ @@ -716,6 +772,7 @@ export const CombinerMax: Combiner.Combiner = BI.CombinerMax as any /** * Combiner that keeps the smallest byte size. * + * @stability unstable * @category math * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Cache.ts b/packages/effect/src/Cache.ts similarity index 97% rename from repos/effect/packages/effect/src/Cache.ts rename to packages/effect/src/Cache.ts index ac406ed5b..6e578eaee 100644 --- a/repos/effect/packages/effect/src/Cache.ts +++ b/packages/effect/src/Cache.ts @@ -6,6 +6,7 @@ * by capacity and optional time-to-live rules. This module includes helpers for * reading, setting, refreshing, invalidating, and inspecting cache contents. * + * @stability stable * @since 4.0.0 */ import * as Context from "./Context.ts" @@ -105,6 +106,7 @@ const TypeId = "~effect/Cache" * actual // => "User-123" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -129,9 +131,14 @@ export interface Cache ex * * An `expiresAt` value of `undefined` means the entry does not expire. * + * Pass the current fiber to `await` when calling it from inside + * `Effect.withFiber`, so the waiter is counted and its cleanup registered in + * that same step. Without it, `await` resolves the fiber itself. + * * @see {@link Cache} for the public cache API that manages entries through * combinators * + * @stability stable * @category models * @since 4.0.0 */ @@ -139,7 +146,7 @@ export interface Entry { expiresAt: number | undefined awaiters: number readonly fiber: Fiber.Fiber - await(this: Entry): Effect.Effect + await(this: Entry, fiber?: Fiber.Fiber): Effect.Effect } /** @@ -184,6 +191,7 @@ export interface Entry { * ``` * * @see {@link make} for a simpler cache constructor with a fixed time-to-live for all entries + * @stability stable * @category constructors * @since 2.0.0 */ @@ -282,6 +290,7 @@ export const makeWith = < * actual // => [{ name: "Ada", email: "ada@example.com" }, { name: "Ada", email: "ada@example.com" }, true] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -414,6 +423,7 @@ const defaultTimeToLive = (_: Exit.Exit, _key: unknown): Duration.Du * actual // => { results: [5, 5, 5], lookupCount: 1 } * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -430,7 +440,7 @@ export const get: { if (!hasExpired(oentry.value, fiber)) { // Move the entry to the end of the map to keep it fresh MutableHashMap.set(self.map, key, oentry.value) - return oentry.value.await() + return oentry.value.await(fiber) } } const entry = new EntryImpl(fiber, self.lookup(key)) @@ -458,7 +468,7 @@ export const get: { if (Number.isFinite(self.capacity)) { checkCapacity(self) } - return entry.await() + return entry.await(fiber) }) ) @@ -477,11 +487,14 @@ class EntryImpl implements Entry { this.expiresAt = undefined } - await(): Effect.Effect { + await(fiber?: Fiber.Fiber): Effect.Effect { + if (fiber === undefined) return core.withFiber((fiber) => this.await(fiber)) const exit = this.fiber.pollUnsafe() if (exit) return exit this.awaiters++ - return effect.onExit(effect.fiberJoin(this.fiber), () => { + // Register cleanup in the same evaluation as the increment, before interruption + // can prevent the returned join effect from starting. + effect.onExitUnsafe(fiber, () => { this.awaiters-- if (this.awaiters > 0 || this.fiber.pollUnsafe()) return effect.void // Detach before interrupting so new lookups do not join the abandoned fiber @@ -489,6 +502,7 @@ class EntryImpl implements Entry { this.onInterrupt?.() return effect.fiberInterrupt(this.fiber) }) + return effect.fiberJoin(this.fiber) } } @@ -616,6 +630,7 @@ const checkCapacity = (self: Cache) => { * actual // => [Option.some(42), 42] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -627,7 +642,7 @@ export const getOption: { (self: Cache, key: Key): Effect.Effect, E> => core.withFiber((fiber) => { const entry = getImpl(self, key, fiber) - return entry ? effect.asSome(entry.await()) : effect.succeedNone + return entry ? effect.asSome(entry.await(fiber)) : effect.succeedNone }) ) @@ -663,6 +678,7 @@ const getImpl = ( * @see {@link get} for triggering or awaiting the cache lookup * @see {@link getOption} for reading an existing entry as an optional effect * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -788,6 +804,7 @@ export const getSuccess: { * actual // => [2, 2, false, true] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -908,6 +925,7 @@ export const set: { * actual // => ["apple: true", "banana: true", "cherry: false", "date: false"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -969,6 +987,7 @@ export const has: { * actual // => { beforeInvalidation: true, afterInvalidation: false, lookupCount: 2 } * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1042,6 +1061,7 @@ export const invalidate: { * actual // => [true, false, false, true, false, false] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1056,7 +1076,7 @@ export const invalidateWhen: { if (oentry === undefined) { return effect.succeed(false) } - return oentry.await().pipe( + return oentry.await(fiber).pipe( effect.map((value) => { if (f(value)) { const current = MutableHashMap.get(self.map, key) @@ -1169,6 +1189,7 @@ export const invalidateWhen: { * actual // => ["value-for-newKey", true] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1207,7 +1228,7 @@ export const refresh: { checkCapacity(self) } }) - return entry.await() + return entry.await(fiber) }) ) @@ -1256,6 +1277,7 @@ export const refresh: { * actual // => [3, true, 0, false, false, false] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1302,6 +1324,7 @@ export const invalidateAll = (self: Cache): Effect.E * actual // => [0, 2, 1] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1337,6 +1360,7 @@ export const size = (self: Cache): Effect.Effect ["cache", "hello", "world"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1381,6 +1405,7 @@ export const keys = (self: Cache): Effect.Effect [1, 2, 3] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1399,6 +1424,7 @@ export const values = (self: Cache): Effect.Effect Cause.fail("Something went wrong") * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -89,6 +93,7 @@ export interface Cause extends Pipeable, Inspectable, Equal { * Cause.isCause("not a cause") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -107,6 +112,7 @@ export const isCause: (self: unknown) => self is Cause = core.isCause * Cause.isReason("not a reason") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -138,6 +144,7 @@ export const isReason: (self: unknown) => self is Reason = core.isCause * } * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -164,6 +171,7 @@ export type Reason = Fail | Die | Interrupt * @see {@link isDieReason} — narrow to `Die` * @see {@link isInterruptReason} — narrow to `Interrupt` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -190,6 +198,7 @@ export const isFailReason: (self: Reason) => self is Fail = core.isFail * @see {@link isFailReason} — narrow to `Fail` * @see {@link isInterruptReason} — narrow to `Interrupt` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -216,6 +225,7 @@ export const isDieReason: (self: Reason) => self is Die = core.isDieReason * @see {@link isFailReason} — narrow to `Fail` * @see {@link isDieReason} — narrow to `Die` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -224,6 +234,7 @@ export const isInterruptReason: (self: Reason) => self is Interrupt = core /** * Companion namespace for the `Cause` interface. * + * @stability stable * @since 2.0.0 */ export declare namespace Cause { @@ -270,6 +281,7 @@ export declare namespace Cause { /** * Companion namespace for the `Reason` type. * + * @stability stable * @since 4.0.0 */ export declare namespace Reason { @@ -320,6 +332,7 @@ export declare namespace Reason { * @see {@link die} for constructing a cause with a single `Die` reason * @see {@link isDieReason} for narrowing a `Reason` to `Die` * + * @stability stable * @category models * @since 2.0.0 */ @@ -355,6 +368,7 @@ export interface Die extends Cause.ReasonProto<"Die"> { * @see {@link fail} for constructing a cause with a single `Fail` reason * @see {@link isFailReason} for narrowing a `Reason` to `Fail` * + * @stability stable * @category models * @since 2.0.0 */ @@ -382,6 +396,7 @@ export interface Fail extends Cause.ReasonProto<"Fail"> { * } * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -422,6 +437,7 @@ export interface Interrupt extends Cause.ReasonProto<"Interrupt"> { * * @see {@link combine} — merge two existing causes * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -452,6 +468,7 @@ export const fromReasons: ( * * @see {@link combine} for merging causes where `empty` acts as the identity * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -476,6 +493,7 @@ export const empty: Cause = core.causeEmpty * @see {@link die} — for untyped defects * @see {@link interrupt} — for fiber interruptions * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -500,6 +518,7 @@ export const fail: (error: E) => Cause = core.causeFail * @see {@link fail} — for typed errors * @see {@link interrupt} — for fiber interruptions * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -520,6 +539,7 @@ export const die: (defect: unknown) => Cause = core.causeDie * @see {@link fail} — for typed errors * @see {@link die} — for untyped defects * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -544,6 +564,7 @@ export const interrupt: (fiberId?: number | undefined) => Cause = effect. * @see {@link makeDieReason} — create a `Die` reason * @see {@link makeInterruptReason} — create an `Interrupt` reason * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -568,6 +589,7 @@ export const makeFailReason = (error: E): Fail => new core.Fail(error) * @see {@link makeFailReason} — create a `Fail` reason * @see {@link makeInterruptReason} — create an `Interrupt` reason * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -593,6 +615,7 @@ export const makeDieReason = (defect: unknown): Die => new core.Die(defect) * @see {@link makeFailReason} — create a `Fail` reason * @see {@link makeDieReason} — create a `Die` reason * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -618,6 +641,7 @@ export const makeInterruptReason: (fiberId?: number | undefined) => Interrupt = * * @see {@link hasInterrupts} — `true` if the cause contains *any* interrupts * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -652,6 +676,7 @@ export const hasInterruptsOnly: (self: Cause) => boolean = effect.hasInter * } * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -686,6 +711,7 @@ export const map: { * @see {@link fromReasons} — build a cause from an array of reasons * @see {@link empty} for the identity cause used when combining * + * @stability stable * @category combining * @since 4.0.0 */ @@ -730,6 +756,7 @@ export const combine: { * @see {@link prettyErrors} — non-lossy conversion to `Array` * @see {@link pretty} — human-readable string rendering * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -755,6 +782,7 @@ export const squash: (self: Cause) => unknown = effect.causeSquash * @see {@link hasDies} — check for defects * @see {@link hasInterrupts} — check for interruptions * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -782,6 +810,7 @@ export const hasFails: (self: Cause) => boolean = effect.hasFails * @see {@link findError} — extract the unwrapped `E` value * @see {@link findDie} — extract the first `Die` reason * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -809,6 +838,7 @@ export const findFail: (self: Cause) => Result.Result, Cause(self: Cause) => Result.Result> = * * @see {@link findError} — `Result`-based variant * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -859,6 +890,7 @@ export const findErrorOption: (input: Cause) => Option = effect.findErr * @see {@link hasFails} — check for typed errors * @see {@link hasInterrupts} — check for interruptions * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -885,6 +917,7 @@ export const hasDies: (self: Cause) => boolean = effect.hasDies * @see {@link findDefect} — extract the unwrapped defect value * @see {@link findFail} — extract the first `Fail` reason * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -911,6 +944,7 @@ export const findDie: (self: Cause) => Result.Result> = effe * @see {@link findDie} — extract the full `Die` reason * @see {@link findError} — extract the first typed error * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -932,6 +966,7 @@ export const findDefect: (self: Cause) => Result.Result> * @see {@link hasFails} — check for typed errors * @see {@link hasDies} — check for defects * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -957,6 +992,7 @@ export const hasInterrupts: (self: Cause) => boolean = effect.hasInterrupt * * @see {@link interruptors} — collect all interrupting fiber IDs as a `Set` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -987,6 +1023,7 @@ export const findInterrupt: (self: Cause) => Result.Result(self: Cause) => ReadonlySet = effect.c * * @see {@link interruptors} — always-succeeding variant * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1062,6 +1100,7 @@ export const filterInterruptors: (self: Cause) => Result.Result(self: Cause, options?: { * * @see {@link prettyErrors} — get the individual `Error` instances * + * @stability stable * @category formatting * @since 2.0.0 */ @@ -1139,6 +1179,7 @@ export const pretty: (cause: Cause) => string = effect.causePretty * await Effect.runPromiseExit(program) // => Exit.fail(error) * ``` * + * @stability stable * @category errors * @since 2.0.0 */ @@ -1159,6 +1200,7 @@ export interface YieldableError extends Error, Pipeable, Inspectable { * Cause.isNoSuchElementError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1167,6 +1209,7 @@ export const isNoSuchElementError: (u: unknown) => u is NoSuchElementError = cor /** * Unique brand for `NoSuchElementError`. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1191,6 +1234,7 @@ export const NoSuchElementErrorTypeId: "~effect/Cause/NoSuchElementError" = core * expected case. This error is mainly for APIs that intentionally turn absence * into a thrown value or failed effect. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1217,6 +1261,7 @@ export interface NoSuchElementError extends YieldableError { * * @see {@link isNoSuchElementError} for checking unknown values * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1234,6 +1279,7 @@ export const NoSuchElementError: new(message?: string) => NoSuchElementError = c * Cause.isDone("not done") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1242,6 +1288,7 @@ export const isDone: (u: unknown) => u is Done = core.isDone /** * Unique brand for `Done` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1280,6 +1327,7 @@ export const DoneTypeId: "~effect/Cause/Done" = core.DoneTypeId * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1292,6 +1340,7 @@ export interface Done { /** * Companion namespace for the `Done` interface. * + * @stability stable * @since 4.0.0 */ export declare namespace Done { @@ -1322,6 +1371,7 @@ export declare namespace Done { * * @see {@link done} — create a failing `Effect` with `Done` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1347,6 +1397,7 @@ export const Done: (value?: A) => Done = core.Done * * @see {@link Done} — create the signal value without an Effect * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1355,6 +1406,7 @@ export const done: (value?: A) => Effect.Effect> = core /** * Unique brand for `TimeoutError`. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1372,6 +1424,7 @@ export const TimeoutErrorTypeId: "~effect/Cause/TimeoutError" = effect.TimeoutEr * Cause.isTimeoutError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1385,6 +1438,7 @@ export const isTimeoutError: (u: unknown) => u is TimeoutError = effect.isTimeou * Produced by `Effect.timeout` and related APIs. Implements * `YieldableError`. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1404,6 +1458,7 @@ export interface TimeoutError extends YieldableError { * new Cause.TimeoutError("Operation timed out").message // => "Operation timed out" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1412,6 +1467,7 @@ export const TimeoutError: new(message?: string) => TimeoutError = effect.Timeou /** * Unique brand for `IllegalArgumentError`. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1429,6 +1485,7 @@ export const IllegalArgumentErrorTypeId: "~effect/Cause/IllegalArgumentError" = * Cause.isIllegalArgumentError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1442,6 +1499,7 @@ export const isIllegalArgumentError: (u: unknown) => u is IllegalArgumentError = * * Implements `YieldableError`. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1461,6 +1519,7 @@ export interface IllegalArgumentError extends YieldableError { * new Cause.IllegalArgumentError("Invalid argument").message // => "Invalid argument" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1478,6 +1537,7 @@ export const IllegalArgumentError: new(message?: string) => IllegalArgumentError * Cause.isExceededCapacityError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1486,6 +1546,7 @@ export const isExceededCapacityError: (u: unknown) => u is ExceededCapacityError /** * Unique brand for `ExceededCapacityError`. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1504,6 +1565,7 @@ export const ExceededCapacityErrorTypeId: "~effect/Cause/ExceededCapacityError" * * Implements `YieldableError`. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1529,6 +1591,7 @@ export interface ExceededCapacityError extends YieldableError { * * @see {@link isExceededCapacityError} for checking unknown values * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1538,6 +1601,7 @@ export const ExceededCapacityError: new(message?: string) => ExceededCapacityErr * Unique brand present on `AsyncFiberError` values and used by * `isAsyncFiberError` for runtime checks. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1558,6 +1622,7 @@ export const AsyncFiberErrorTypeId: "~effect/Cause/AsyncFiberError" = effect.Asy * Cause.isAsyncFiberError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1588,6 +1653,7 @@ export const isAsyncFiberError: (u: unknown) => u is AsyncFiberError = effect.is * isSameFiber // => true * ``` * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1618,6 +1684,7 @@ export interface AsyncFiberError extends YieldableError { * * @see {@link isAsyncFiberError} for checking unknown values * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1626,6 +1693,7 @@ export const AsyncFiberError: new(fiber: Fiber) => AsyncFiberE /** * Unique brand for `UnknownError`. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -1643,6 +1711,7 @@ export const UnknownErrorTypeId: "~effect/Cause/UnknownError" = effect.UnknownEr * Cause.isUnknownError("nope") // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1657,6 +1726,7 @@ export const isUnknownError: (u: unknown) => u is UnknownError = effect.isUnknow * typed error. The original value is stored in the `cause` property inherited * from `Error`. Implements `YieldableError`. * + * @stability stable * @category errors * @since 4.0.0 */ @@ -1678,6 +1748,7 @@ export interface UnknownError extends YieldableError { * new Cause.UnknownError({ raw: true }, "Unexpected value").message // => "Unexpected value" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1714,6 +1785,7 @@ export const UnknownError: new(cause: unknown, message?: string) => UnknownError * @see {@link annotations} for reading merged annotations from a cause * @see {@link reasonAnnotations} for reading annotations from a single reason * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -1752,6 +1824,7 @@ export const annotate: { * * @see {@link annotations} — merged annotations from all reasons in a cause * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -1786,6 +1859,7 @@ export const reasonAnnotations: (self: Reason) => Context.Context = * * @see {@link reasonAnnotations} — annotations from a single reason * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -1809,6 +1883,7 @@ export const annotations: (self: Cause) => Context.Context = effect * @see {@link annotations} for reading merged annotations from a cause * @see {@link InterruptorStackTrace} for the interrupt-specific stack-frame annotation * + * @stability stable * @category services * @since 4.0.0 */ @@ -1831,6 +1906,7 @@ export class StackTrace extends Context.Service()("effec * @see {@link reasonAnnotations} for reading annotations from a single reason * @see {@link annotate} for attaching annotations to a cause * + * @stability stable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Channel.ts b/packages/effect/src/Channel.ts similarity index 98% rename from repos/effect/packages/effect/src/Channel.ts rename to packages/effect/src/Channel.ts index 53357cc35..c6f3a68a1 100644 --- a/repos/effect/packages/effect/src/Channel.ts +++ b/packages/effect/src/Channel.ts @@ -7,6 +7,7 @@ * higher-level modules instead. This module is useful when implementing stream * operators or specialized streaming workflows. * + * @stability stable * @since 2.0.0 */ // @effect-diagnostics returnEffectInGen:off @@ -50,6 +51,7 @@ import type * as Unify from "./Unify.ts" /** * String literal type used as the unique brand for `Channel` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -59,6 +61,7 @@ export type TypeId = "~effect/Channel" * Runtime identifier stored on `Channel` values and used by `isChannel` to * recognize them. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -77,6 +80,7 @@ export const TypeId: TypeId = "~effect/Channel" * Channel.isChannel("not a channel") // => false * ``` * + * @stability stable * @category guards * @since 3.5.4 */ @@ -135,6 +139,7 @@ export const isChannel = ( * Effect.runSync(Channel.runCollect(channel)) // => [1] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -161,6 +166,7 @@ export interface Channel< * or generic return types that include channels. Users normally do not need to * reference this interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -181,6 +187,7 @@ export interface ChannelUnify extends Ef * environment type parameters. Users normally do not need to reference this * interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -202,6 +209,7 @@ type TagsWithReason = { * contravariant. This is type-level machinery and is not used directly at * runtime. * + * @stability stable * @category models * @since 2.0.0 */ @@ -226,6 +234,7 @@ export interface Variance< * `_InElem`, `_InErr`, and `_InDone` fields are contravariant. Users normally * do not need to reference this interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -278,6 +287,7 @@ const ChannelProto = { * await Effect.runPromise(Channel.runCollect(channel)) // => [] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -322,6 +332,7 @@ export const fromTransform = [2, 4, 6] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -374,6 +385,7 @@ export const transformPull = < * await Effect.runPromise(Channel.runCollect(channel)) // => [42] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -394,6 +406,7 @@ export const fromPull = ( * when the channel completes. * * @see {@link fromTransform} for a simpler transformation without a forked scope + * @stability stable * @category constructors * @since 4.0.0 */ @@ -431,6 +444,7 @@ export const fromTransformBracket = [42] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -452,6 +466,7 @@ export const toTransform = 4096 * ``` * + * @stability stable * @category constants * @since 4.0.0 */ @@ -470,7 +485,12 @@ const asyncQueue = ( strategy: options?.strategy }).pipe( Effect.tap((queue) => Scope.addFinalizer(scope, Queue.shutdown(queue))), - Effect.tap((queue) => Effect.forkIn(Scope.provide(f(queue), scope), scope)) + Effect.tap((queue) => + Scope.provide(f(queue), scope).pipe( + Effect.catchCause((cause) => Queue.failCause(queue, cause)), + Effect.forkIn(scope) + ) + ) ) /** @@ -492,6 +512,7 @@ const asyncQueue = ( * await Effect.runPromise(Channel.runCollect(channel)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -520,6 +541,7 @@ export const callback = ( * await Effect.runPromise(Channel.runCollect(channel)) // => [[1, 2]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -544,6 +566,7 @@ export const callbackArray = ( * Effect.runSync(Channel.runCollect(channel)) // => [42] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -576,6 +599,7 @@ export const suspend = ( * const observed = [await Effect.runPromise(Channel.runCollect(channel)), released] // => [["RESOURCE"], ["resource"]] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -591,8 +615,10 @@ export const acquireUseRelease = { + option = Option.some(value) + return value + })) return yield* toTransform(use(value))(upstream, scope) }) ) @@ -620,6 +646,7 @@ export const acquireUseRelease = [["resource"], ["resource"]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -653,6 +680,7 @@ export const acquireRelease: { * Effect.runSync(Channel.runCollect(channel)) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -679,6 +707,7 @@ export const fromIterator = (iterator: LazyArg>): Channel [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -701,6 +730,7 @@ export const fromArray = (array: ReadonlyArray): Channel => * Effect.runSync(Channel.runCollect(channel)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -754,6 +784,7 @@ export const fromChunk = (chunk: Chunk.Chunk): Channel => fromArray(Chu * Effect.runSync(Channel.runCollect(fibChannel)) // => [[0, 1, 1], [2, 3]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -799,6 +830,7 @@ export const fromIteratorArray = ( * Effect.runSync(Channel.runCollect(channel)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -823,6 +855,7 @@ export const fromIterable = (iterable: Iterable): Channel [[1, 2, 3, 4], [5]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -843,6 +876,7 @@ export const fromIterableArray = ( * Effect.runSync(Channel.runCollect(channel)) // => [42] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -860,6 +894,7 @@ export const succeed = (value: A): Channel => fromEffect(Effect.succeed(va * Effect.runSync(Channel.runCollect(channel)) // => [] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -868,6 +903,7 @@ export const end = (value: A): Channel => fromPull(Effect.su /** * Creates a `Channel` that immediately ends with the lazily evaluated value. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -891,6 +927,7 @@ export const endSync = (evaluate: LazyArg): Channel => * Effect.runSync(Channel.runCollect(channel)) // => ["request-1"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -918,6 +955,7 @@ export const sync = (evaluate: LazyArg): Channel => fromEffect(Effect.s * Effect.runSync(Channel.runCollect(conditionalChannel(true))) // => ["data"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -947,6 +985,7 @@ export const empty: Channel = fromPull(Effect.succeed(Cause.done())) * Channel.isChannel(conditionalChannel(false)) // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -964,6 +1003,7 @@ export const never: Channel = fromPull(Effect.succeed(Effec * Effect.runSync(Effect.exit(Channel.runCollect(failedChannel))) // => Exit.fail("Something went wrong") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -994,6 +1034,7 @@ export const fail = (error: E): Channel => fromPull(Effect.s * ] // => [Exit.fail("Error after attempt 1"), 1] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1017,6 +1058,7 @@ export const failSync = (evaluate: LazyArg): Channel => f * Effect.runSync(Effect.exit(Channel.runCollect(failedChannel))) // => Exit.failCause(simpleCause) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1044,6 +1086,7 @@ export const failCause = (cause: Cause.Cause): Channel => * ] // => [Exit.fail("Runtime error after attempt 1"), 1] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1064,6 +1107,7 @@ export const failCauseSync = ( * Effect.runSync(Effect.exit(Channel.runCollect(diedChannel))) // => Exit.failCause(Cause.die(defect)) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1083,6 +1127,7 @@ export const die = (defect: unknown): Channel => failCause( * Effect.runSync(Channel.runCollect(successChannel)) // => ["Hello from effect!"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1108,6 +1153,7 @@ export const fromEffect = ( * * If the effect fails, the channel fails with the effect's error. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1119,6 +1165,7 @@ export const fromEffectDone = ( /** * Uses an effect and discards its result. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1135,6 +1182,7 @@ export const fromEffectDrain = ( * `Take` fails the channel. A done `Take` completes the channel with its done * value. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1161,6 +1209,7 @@ export const fromEffectTake = ( * await Effect.runPromise(program) // => ["item1", "item2", "item3"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1186,6 +1235,7 @@ export const fromQueue = ( * await Effect.runPromise(program) // => [[1, 2, 3, 4]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1197,6 +1247,7 @@ export const fromQueueArray = ( * Creates a channel that forwards upstream input elements, input errors, and * the upstream done value unchanged. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1246,6 +1297,7 @@ export const identity = (): Channel( * Option.map(result, ({ count, sum, average, min, max }) => ({ count, sum, average, min, max })) // => Option.some({ count: 1, sum: 10, average: 10, min: 10, max: 10 }) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1466,6 +1519,7 @@ export const fromSubscriptionArray = ( * const result = await Effect.runPromise(event) // => Option.some({ type: "user.created", payload: {}, timestamp: 1, processed: true, processedAt: 2 }) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1608,6 +1662,7 @@ export const fromPubSub = ( * Option.map(result, (batch) => [batch.batchId, batch.totalEntries, batch.infoCount]) // => Option.some(["1-1", 1, 1]) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1622,6 +1677,7 @@ export const fromPubSubArray = (pubsub: PubSub.PubSub): Channel( /** * Creates a Channel from a Schedule. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1662,6 +1719,7 @@ export const fromSchedule = ( * await Effect.runPromise(Channel.runCollect(channel)) // => [[1]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1752,6 +1810,7 @@ export const pullIntoWritableStream = (options: { * written // => [1, 2] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1791,6 +1850,7 @@ export const fromWritableStream = (options: { * await Effect.runPromise(program) // => [[2], [4]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1874,6 +1934,7 @@ const readableStreamToPullUnsafe = (options: { * are converted with `onError`. If the channel scope closes early and the * iterator has a `return` method, that method is called. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1905,6 +1966,7 @@ export const fromAsyncIterable = ( * rejected iterator errors are converted with `onError`. If the channel scope * closes early and the iterator has a `return` method, that method is called. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1950,6 +2012,7 @@ export const fromAsyncIterableArray = ( * Effect.runSync(Channel.runCollect(displayChannel)) // => [{ displayName: "User: Alice", isActive: true }, { displayName: "User: Bob", isActive: true }] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1979,6 +2042,7 @@ export const map: { /** * Maps the done value of this channel using the specified function. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2008,6 +2072,7 @@ export const mapDone: { * Use when the terminal done value transformation needs services or can fail, * while emitted elements should pass through unchanged. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2067,6 +2132,7 @@ const concurrencyIsSequential = ( * await Effect.runPromise(Channel.runCollect(processedChannel)) // => [1, 4, 9, 16, 25] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2228,6 +2294,7 @@ const mapEffectConcurrent = < * Returns a new channel which is the same as this one but applies the given * function to the input channel’s input elements. * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2235,8 +2302,8 @@ export const mapInput: { ( f: (i: InElem2) => Effect.Effect ): ( - self: Channel - ) => Channel + self: Channel + ) => Channel ( self: Channel, f: (i: InElem2) => Effect.Effect @@ -2256,6 +2323,7 @@ export const mapInput: { * Returns a new channel which is the same as this one but applies the given * function to the input errors. * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2315,6 +2383,7 @@ export const mapInputError: { * const observed = [await Effect.runPromise(Channel.runCollect(tappedChannel)), processed] // => [[1, 2, 3], [1, 2, 3]] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2379,6 +2448,7 @@ export const tap: { * Effect.runSync(Channel.runCollect(flatMappedChannel)) // => ["item-1-0", "item-2-0", "item-2-1", "item-3-0", "item-3-1", "item-3-2"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2579,6 +2649,7 @@ const flatMapConcurrent = < * Effect.runSync(Channel.runCollect(numberChannel)) // => [1, 2, 3, "Completed processing"] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2693,6 +2764,7 @@ export const concatWith: { * Effect.runSync(Channel.runCollect(concatenatedChannel)) // => [1, 2, 3, "a", "b", "c"] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2779,6 +2851,7 @@ export const concat: { * left and right channels. It returns the next output element together with the * next state. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2897,6 +2970,7 @@ export const combine: { * the source completes before emitting an element, the fallback function * receives the source done value and returns the replacement channel. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -3020,6 +3094,7 @@ export const orElseIfEmpty: { * Effect.runSync(Channel.runCollect(flattenedChannel)) // => [1, 2, 3, 4, 5, 6] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -3075,6 +3150,7 @@ export const flatten = < * Effect.runSync(Channel.runCollect(flattenedChannel)) // => [1, 2, 3, 4, 5, 6, 7, 8, 9] * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -3126,6 +3202,7 @@ export const flattenArray = < * Output `Take` values are emitted as non-empty arrays. Failed `Take` values * fail the returned channel. Done `Take` values complete the returned channel. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -3162,6 +3239,7 @@ export const flattenTake = < * Effect.runSync(Channel.runCollect(drainedChannel)) // => [] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -3189,6 +3267,7 @@ export const drain = < /** * Repeats this channel according to the provided schedule. * + * @stability stable * @category repetition * @since 4.0.0 */ @@ -3260,6 +3339,7 @@ const repeatLoop = ( * fails. If the schedule finishes, the returned channel completes with the * schedule output. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -3337,6 +3418,7 @@ export const schedule: { * Effect.runSync(Channel.runCollect(numbersOnlyChannel)) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3393,6 +3475,7 @@ export const filter: { * @see {@link filterMapEffect} for using an effectful `Filter` * @see {@link filterMapArray} for filtering arrays of output elements * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3437,6 +3520,7 @@ export const filterMap: { * for which the predicate succeeds with `false` are discarded. Predicate * failures fail the returned channel. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3489,6 +3573,7 @@ export const filterEffect: { * @see {@link mapEffect} for effectfully transforming every output element * @see {@link filterMapArrayEffect} for effectful filtering of array outputs * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3555,6 +3640,7 @@ export const filterMapEffect: { * Effect.runSync(Channel.runCollect(filteredOddChannel)) // => [[2, 4]] * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3606,6 +3692,7 @@ export const filterArray: { * Successful filter results are kept as mapped values. Failed filter results * are removed from the array. Arrays that become empty are discarded. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3655,6 +3742,7 @@ export const filterMapArray: { * for which the predicate succeeds with `true` are kept. Arrays that become * empty are discarded. Predicate failures fail the returned channel. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3697,6 +3785,7 @@ export const filterArrayEffect: { * are removed from the array. Arrays that become empty are discarded. Failures * from the effectful filter fail the returned channel. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -3763,6 +3852,7 @@ export const filterMapArrayEffect: { * Effect.runSync(Channel.runCollect(asyncMapAccum)) // => ["1-processed", "1", "2-processed", "12", "3-processed", "123", "4-processed", "1234"] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -3896,6 +3986,7 @@ export const mapAccum: { * Effect.runSync(Channel.runCollect(sentenceChannel)) // => ["", "hello", "hello world", "hello world from", "hello world from effect"] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -3974,6 +4065,7 @@ export const scan: { * await Effect.runPromise(Channel.runCollect(errorHandlingScan)) // => [0, 1, 3, 6, 10] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -4057,6 +4149,7 @@ export const scanEffect: { * Effect.runSync(Channel.runCollect(recoveredChannel)) // => ["Recovered from failure"] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4175,6 +4268,7 @@ export const catchCause: { * Effect.runSync(Channel.runCollect(channel)) // => ["recovered: boom"] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4261,6 +4355,7 @@ export const catchDefect: { * Use this for observing failures, such as logging or metrics. If the observer * effect fails, that failure can fail the returned channel. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4349,6 +4444,7 @@ export const tapCause: { * @see {@link catchCause} for recovering from every cause * @see {@link catchIf} for recovering from typed channel errors * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4475,6 +4571,7 @@ export const catchCauseIf: { * @see {@link catchFilter} for selecting typed errors with a `Filter` * @see {@link catchCause} for recovering from every cause * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4676,6 +4773,7 @@ export { /** * Recovers from typed channel errors by running a fallback channel. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4691,6 +4789,7 @@ export { * The effect is not run for normal channel completion. If the observer effect * fails, that failure can fail the returned channel. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4787,6 +4886,7 @@ export const tapError: { * @see {@link catchTag} for selecting tagged typed errors * @see {@link catchCauseFilter} for selecting full causes with a `Filter` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5030,6 +5130,7 @@ export const catchIf: { * @see {@link catchTag} for selecting tagged typed errors * @see {@link catchCauseFilter} for selecting full causes with a `Filter` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5189,6 +5290,7 @@ export const catchFilter: { * errors are handled by `orElse` when provided. Without `orElse`, * non-matching errors are re-failed. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5367,6 +5469,7 @@ export const catchTag: { * Effect.runSync(Channel.runCollect(recovered)) // => ["retry: 60"] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5548,6 +5651,7 @@ export const catchReason: { /** * Catches multiple reasons within a tagged error using an object of handlers. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5748,6 +5852,7 @@ export const catchReasons: { * Effect.runSync(Effect.exit(Channel.runCollect(unwrapped))) // => Exit.fail(reason) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5822,6 +5927,7 @@ export const unwrapReason: { * value of the returned channel is created by applying the specified function * to the failure value of this channel. * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5863,6 +5969,7 @@ export const mapError: { * Effect.runSync(Effect.exit(Channel.runCollect(fatalChannel))) // => Exit.failCause(Cause.die(error)) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5885,6 +5992,7 @@ export const orDie = < * * Use the `log` option to emit the full {@link Cause} when the channel fails. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5947,6 +6055,7 @@ const ignoreCause_ = < * * Use the `log` option to emit the full {@link Cause} when the channel fails. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5982,6 +6091,7 @@ export const ignoreCause: < * Returns a new channel that retries this channel according to the specified * schedule whenever it fails. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -6080,6 +6190,7 @@ export const retry: { * await Effect.runPromise(Channel.runCollect(switchedChannel)) // => ["value-3"] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -6208,6 +6319,7 @@ export const switchMap: { * await Effect.runPromise(Channel.runCollect(mergedChannel)) // => [1, 2, 3, 4, 5, 6] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -6333,17 +6445,17 @@ export const mergeAll: { Effect.flatMap((value) => Queue.offer(queue, value)), Effect.forever({ disableYield: true }), Effect.onError(Effect.fnUntraced(function*(cause) { - const halt = Pull.filterDone(cause) - yield* Effect.exit(Scope.close( - childScope, - !Result.isFailure(halt) ? Exit.succeed(halt.success.value) : Exit.failCause(halt.failure) - )) + const exit = Pull.doneExitFromCause(cause) + const closeExit = yield* Effect.exit(Scope.close(childScope, exit)) if (!fibers.has(fiber)) return + // Publish usage and finalizer failures before releasing the + // permit or opening the latch, so the outer channel cannot + // complete ahead of them. + const failure = Exit.asVoidAll([exit, closeExit]) + if (Exit.isFailure(failure)) yield* Queue.failCause(queue, failure.cause) fibers.delete(fiber) if (semaphore) yield* semaphore.release(1) if (fibers.size === 0) yield* doneLatch.open - if (Result.isSuccess(halt)) return - return yield* Queue.failCause(queue, cause as any) })), Effect.forkChild ) @@ -6379,6 +6491,7 @@ export const mergeAll: { * const strategies: Array = ["left", "right", "both", "either"] // => ["left", "right", "both", "either"] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -6404,6 +6517,7 @@ export type HaltStrategy = "left" | "right" | "both" | "either" * values.map(String).sort() // => ["1", "2", "3", "a", "b", "c"] * ``` * + * @stability stable * @category combining * @since 4.0.0 */ @@ -6531,9 +6645,11 @@ export const merge: { ) ), Effect.onError((cause) => - Effect.andThen( - Scope.close(scope, Pull.doneExitFromCause(cause)), - onExit(side, cause) + Effect.onExitPrimitive(Pull.doneExitFromCause(cause), (exit) => Scope.close(scope, exit)).pipe( + Effect.matchCauseEffect({ + onFailure: (cause) => onExit(side, cause), + onSuccess: () => onExit(side, cause) + }) ) ), Effect.forkIn(forkedScope) @@ -6557,6 +6673,7 @@ export const merge: { * The effect's successful value is ignored. If the effect fails while the * channel is running, the returned channel fails with that error. * + * @stability stable * @category combining * @since 4.0.0 */ @@ -6603,6 +6720,7 @@ export const mergeEffect: { * result // => ["hello", "world"] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -6714,10 +6832,14 @@ export const splitLines = (): Channel< * span `Uint8Array` boundaries. The optional `encoding` and `options` are * passed to `TextDecoder`. * + * @stability stable * @category decoding * @since 4.0.0 */ -export const decodeText = (encoding?: string, options?: TextDecoderOptions): Channel< +export const decodeText = ( + encoding?: string, + options?: ConstructorParameters[1] +): Channel< Arr.NonEmptyReadonlyArray, Err, Done, @@ -6740,6 +6862,7 @@ export const decodeText = (encoding?: string, options?: TextDecoderOp * * Each string inside an emitted array is encoded independently. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -6783,6 +6906,7 @@ export const encodeText = (): Channel< * Effect.runSync(Channel.runCollect(pipedChannel)) // => [2, 4, 6] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -6832,6 +6956,7 @@ export const pipeTo: { * Effect.runSync(Effect.exit(Channel.runCollect(safePipedChannel))) // => Exit.fail(error) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -6895,6 +7020,7 @@ export const pipeToOrFail: { * Effect.runSync(Channel.runCollect(unwrappedChannel)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -6917,6 +7043,7 @@ export const unwrap = ( * await Effect.runPromise(Channel.runCollect(embeddedChannel)) // => [1, 2, 3] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -7014,6 +7142,7 @@ export const embedInput: { * * @see {@link bufferArray} for buffering elements from array outputs * + * @stability stable * @category buffering * @since 2.0.0 */ @@ -7080,6 +7209,7 @@ export const buffer: { * * @see {@link buffer} for buffering output elements without flattening arrays * + * @stability stable * @category buffering * @since 4.0.0 */ @@ -7136,6 +7266,7 @@ export const bufferArray: { * channel's done value. If the channel completes first, the original channel's * done value is preserved. * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7169,6 +7300,7 @@ export const interruptWhen: { * fails with that error. If the channel completes first, the channel's done * value is preserved. * + * @stability stable * @category interruption * @since 4.0.0 */ @@ -7208,6 +7340,7 @@ export const haltWhen: { * The finalizer receives the failure `Cause`. The original channel failure is * preserved. The finalizer itself must not fail. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -7252,6 +7385,7 @@ export const onError: { * const observed = [await Effect.runPromise(Channel.runCollect(channelWithExit)), exits] // => [[1, 2, 3], [Exit.void]] * ``` * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -7283,6 +7417,7 @@ export const onExit: { * The effect's successful value is ignored. If the effect fails, the returned * channel fails before running the source channel. * + * @stability stable * @category hooks * @since 4.0.0 */ @@ -7315,6 +7450,7 @@ export const onStart: { * emitted unchanged. The effect is not run if the channel completes without * emitting an element. * + * @stability stable * @category hooks * @since 4.0.0 */ @@ -7351,6 +7487,7 @@ export const onFirst: { * not run when the channel fails. If the effect fails, the returned channel * fails with that error. * + * @stability stable * @category hooks * @since 4.0.0 */ @@ -7399,6 +7536,7 @@ export const onEnd: { * const observed = [await Effect.runPromise(Channel.runCollect(channelWithCleanup)), events] // => [[1, 2, 3], ["cleanup"]] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -7443,6 +7581,7 @@ const runWith = < /** * Creates a channel from the specified services. * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -7457,6 +7596,7 @@ export const contextWith = ( /** * The starting channel for Do notation, emitting an empty object. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -7776,6 +7923,7 @@ export { /** * Adds a computed field to each object emitted by a channel. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -7792,6 +7940,7 @@ export { * channel's output becomes the value of the new field. `options.concurrency` * and `options.bufferSize` control how derived channels are flattened. * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -7898,6 +8047,7 @@ export const bind: { * @see {@link bind} for adding a field produced by another channel * @see {@link let_ let} for adding a computed field * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -7959,6 +8109,7 @@ export const bindTo: { * Effect.runSync(countEffect) // => 5 * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -7988,6 +8139,7 @@ export const runCount = ( * Effect.runSync(drainEffect) // => "completed" * ``` * + * @stability stable * @category running * @since 2.0.0 */ @@ -8021,6 +8173,7 @@ export const runDrain = ( * processed // => [1, 2, 3] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -8052,6 +8205,7 @@ export const runForEach: { * Returning `true` continues consuming the channel. Returning `false` stops * consumption early. The returned effect completes with `void`. * + * @stability stable * @category running * @since 4.0.0 */ @@ -8101,6 +8255,7 @@ export const runForEachWhile: { * Array.from(bytes) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -8158,6 +8313,7 @@ export const mkUint8Array = ( * Effect.runSync(collectEffect) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category running * @since 2.0.0 */ @@ -8178,6 +8334,7 @@ export const runCollect = ( * Returns `Option.some` with the first output element, or `Option.none` if the * channel completes without emitting output. * + * @stability stable * @category running * @since 4.0.0 */ @@ -8205,6 +8362,7 @@ export const runHead = ( * Returns `Option.some` with the last emitted element, or `Option.none` if the * channel completes without emitting output. * + * @stability stable * @category running * @since 4.0.0 */ @@ -8249,6 +8407,7 @@ export const runLast = ( * Effect.runSync(sumEffect) // => 15 * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -8299,6 +8458,7 @@ export const runFold: { * the effectful accumulator function. The returned effect succeeds with the * final accumulator value. * + * @stability stable * @category running * @since 4.0.0 */ @@ -8366,6 +8526,7 @@ export const runFoldEffect: { * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -8416,6 +8577,7 @@ export const toPull: ( * await Effect.runPromise(Effect.scoped(scopedPullEffect)) // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8433,6 +8595,7 @@ export const toPullScoped = ( * queue is failed with the channel's cause. The returned effect itself * completes with `void`. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8476,6 +8639,7 @@ export const runIntoQueue: { * queue is failed with the channel's cause. The returned effect itself * completes with `void`. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8539,6 +8703,7 @@ export const runIntoQueueArray: { * await Effect.runPromise(program) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -8593,6 +8758,7 @@ export const toQueue: { * completion and failure are signaled through the queue. The queue is shut down * when the surrounding scope closes. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8646,6 +8812,7 @@ export const toQueueArray: { * `shutdownOnEnd` indicates whether the PubSub should be shut down when the * channel ends. By default this is `true`. * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -8708,6 +8875,7 @@ export const toPubSub: { * The channel's output values are published as individual PubSub messages. Use * `options.shutdownOnEnd` to shut down the PubSub when channel execution ends. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8719,14 +8887,14 @@ export const runIntoPubSub: { } | undefined ): ( self: Channel - ) => Effect.Effect + ) => Effect.Effect ( self: Channel, pubsub: PubSub.PubSub, options?: { readonly shutdownOnEnd?: boolean | undefined } | undefined - ): Effect.Effect + ): Effect.Effect } = dual( (args) => isChannel(args[0]), ( @@ -8772,6 +8940,7 @@ const makePubSub = ( * PubSub message. `shutdownOnEnd` indicates whether the PubSub should be shut * down when the channel ends. By default this is `true`. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8836,6 +9005,7 @@ export const toPubSubArray: { * PubSub message. Use `options.shutdownOnEnd` to shut down the PubSub when * channel execution ends. * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -8879,6 +9049,7 @@ export const runIntoPubSubArray: { * subscriber, including one that subscribes later, observes completion or * failure once it has consumed its buffered values. * + * @stability stable * @category destructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ChannelSchema.ts b/packages/effect/src/ChannelSchema.ts similarity index 98% rename from repos/effect/packages/effect/src/ChannelSchema.ts rename to packages/effect/src/ChannelSchema.ts index b2a84f438..ac4761476 100644 --- a/repos/effect/packages/effect/src/ChannelSchema.ts +++ b/packages/effect/src/ChannelSchema.ts @@ -5,6 +5,7 @@ * work with schema-typed input and output while the inner channel uses encoded * values. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "./Array.ts" @@ -31,6 +32,7 @@ import type * as SchemaAST from "./SchemaAST.ts" * @see {@link encodeUnknown} for encoded output chunks that should be typed as `unknown` * @see {@link decode} for the inverse channel that decodes encoded chunks into schema values * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -61,6 +63,7 @@ export const encode = ( * * @see {@link encode} for the variant that preserves the schema encoded type * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -93,6 +96,7 @@ export const encodeUnknown: ( * @see {@link decodeUnknown} for boundaries where the encoded input side is intentionally untyped * @see {@link encode} for the inverse adapter that encodes typed schema values * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -129,6 +133,7 @@ export const decode = ( * * @see {@link decode} for the typed variant that preserves the schema's encoded type * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -165,6 +170,7 @@ export const decodeUnknown: ( * @see {@link encode} for encoding typed chunks at one-way channel boundaries * @see {@link decode} for decoding encoded chunks at one-way channel boundaries * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -259,6 +265,7 @@ export const duplex: { * * @see {@link duplex} for the variant that preserves the schema encoded types on the wrapped channel * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Chunk.ts b/packages/effect/src/Chunk.ts similarity index 97% rename from repos/effect/packages/effect/src/Chunk.ts rename to packages/effect/src/Chunk.ts index 8555f3253..364d98e47 100644 --- a/repos/effect/packages/effect/src/Chunk.ts +++ b/packages/effect/src/Chunk.ts @@ -7,6 +7,7 @@ * creating, reading, slicing, mapping, filtering, sorting, zipping, combining, * and converting chunks to and from arrays and iterables. * + * @stability stable * @since 2.0.0 */ import * as RA from "./Array.ts" @@ -46,6 +47,7 @@ const TypeId = "~effect/Chunk" * Chunk.toArray(chunk) // => [1, 2, 3] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -73,6 +75,7 @@ export interface Chunk extends Iterable, Equal.Equal, Pipeable, Inspec * Chunk.lastNonEmpty(nonEmptyChunk) // => 3 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -91,6 +94,7 @@ export interface NonEmptyChunk extends Chunk, NonEmptyIterable {} * // Equivalent to: Chunk * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -165,6 +169,7 @@ const emptyArray: ReadonlyArray = [] * eq(chunk1, chunk3) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -284,6 +289,7 @@ const makeChunk = (backing: Backing): Chunk => { * Chunk.isChunk("string") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -305,6 +311,7 @@ const _empty = makeChunk({ _tag: "IEmpty" }) * Chunk.size(Chunk.empty()) // => 0 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -321,6 +328,7 @@ export const empty: () => Chunk = () => _empty * Chunk.toArray(Chunk.make(1, 2, 3, 4)) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -338,6 +346,7 @@ export const make = ]>(...as: As): NonEm * Chunk.toArray(Chunk.of("hello")) // => ["hello"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -354,6 +363,7 @@ export const of = (a: A): NonEmptyChunk => makeChunk({ _tag: "ISingleton", * Chunk.toArray(Chunk.fromIterable([1, 2, 3])) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -403,6 +413,7 @@ const toArray_ = (self: Chunk): Array => toReadonlyArray(self).slice() * Chunk.toArray(Chunk.empty()) // => [] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -454,6 +465,7 @@ const toReadonlyArray_ = (self: Chunk): ReadonlyArray => { * Chunk.toReadonlyArray(Chunk.empty()) // => [] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -499,6 +511,7 @@ const reverseChunk = (self: Chunk): Chunk => { * Chunk.toArray(Chunk.reverse(chunk)) // => [3, 2, 1] * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -523,6 +536,7 @@ export const reverse: >(self: S) => Chunk.With Option.some("c") * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -561,6 +575,7 @@ export const get: { * Chunk.toArray(chunk) // => [999, 2, 3, 4, 5] * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -592,6 +607,7 @@ export const fromArrayUnsafe = (self: ReadonlyArray): Chunk => * Chunk.isNonEmpty(chunk) // => true * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -624,6 +640,7 @@ export const fromNonEmptyArrayUnsafe = (self: NonEmptyReadonlyArray): NonE * Option.isNone(Chunk.get(chunk, 10)) // => true * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -683,6 +700,7 @@ export const getUnsafe: { * @see {@link prepend} for adding one element before the existing elements * @see {@link appendAll} for appending all elements from another chunk * + * @stability stable * @category combining * @since 2.0.0 */ @@ -707,6 +725,7 @@ export const append: { * Chunk.toArray(Chunk.prepend(emptyChunk, "first")) // => ["first"] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -728,6 +747,7 @@ export const prepend: { * Chunk.toArray(Chunk.take(chunk, 3)) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -786,6 +806,7 @@ export const take: { * Chunk.toArray(Chunk.drop(chunk, 2)) // => [3, 4, 5] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -843,6 +864,7 @@ export const drop: { * Chunk.toArray(Chunk.dropRight(chunk, 2)) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -866,6 +888,7 @@ export const dropRight: { * Chunk.toArray(Chunk.dropWhile(chunk, (n) => n < 3)) // => [3, 4, 5] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -897,6 +920,7 @@ export const dropWhile: { * ) // => ["a", "b", 1, 2] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -932,6 +956,7 @@ export const prependAll: { * @see {@link prependAll} for concatenating chunks in the opposite order * @see {@link append} for adding a single element to the end * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1006,6 +1031,7 @@ export const appendAll: { * Chunk.toArray(evenIndexNumbers) // => [1] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1045,6 +1071,7 @@ export const filterMap: { * Chunk.toArray(numbers) // => [42, 100] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1078,6 +1105,7 @@ export const filter: { * })) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1109,6 +1137,7 @@ export const filterMapWhile: { * Chunk.toArray(Chunk.compact(chunk)) // => [1, 3] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1147,6 +1176,7 @@ export const compact = (self: Chunk>): Chunk => { * Chunk.toArray(indexed) // => [1, 3, 5] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1200,6 +1230,7 @@ export const flatMap: { * indexed // => ["Index 0: 1", "Index 1: 2", "Index 2: 3", "Index 3: 4"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1232,6 +1263,7 @@ export const forEach: { * Chunk.toArray(Chunk.flatten(withEmpty)) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1273,6 +1305,7 @@ export const flatten: >>(self: S) => Chunk.Flatten * * @see {@link split} for splitting into a target number of chunks instead of a fixed chunk size * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1323,6 +1356,7 @@ export const chunksOf: { * Chunk.toArray(Chunk.intersection(chunk3, chunk4)) // => [] * ``` * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -1347,6 +1381,7 @@ export const intersection: { * Chunk.isEmpty(Chunk.make(1, 2, 3)) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1364,6 +1399,7 @@ export const isEmpty = (self: Chunk): boolean => self.length === 0 * Chunk.isNonEmpty(Chunk.make(1, 2, 3)) // => true * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -1381,6 +1417,7 @@ export const isNonEmpty = (self: Chunk): self is NonEmptyChunk => self. * Chunk.head(Chunk.make(1, 2, 3)) // => Option.some(1) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1413,6 +1450,7 @@ export const head: (self: Chunk) => Option = get(0) * Option.isNone(Chunk.head(Chunk.empty())) // => true * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -1436,6 +1474,7 @@ export const headUnsafe = (self: Chunk): A => getUnsafe(self, 0) * // Chunk.headNonEmpty(Chunk.empty()) // TypeScript error * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1453,6 +1492,7 @@ export const headNonEmpty: (self: NonEmptyChunk) => A = headUnsafe * Chunk.last(Chunk.make(1, 2, 3)) // => Option.some(3) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1485,6 +1525,7 @@ export const last = (self: Chunk): Option => get(self, self.length - 1) * Option.isNone(Chunk.last(Chunk.empty())) // => true * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -1508,6 +1549,7 @@ export const lastUnsafe = (self: Chunk): A => getUnsafe(self, self.length * // Chunk.lastNonEmpty(Chunk.empty()) // TypeScript error * ``` * + * @stability stable * @category getters * @since 3.4.0 */ @@ -1530,6 +1572,7 @@ export const lastNonEmpty: (self: NonEmptyChunk) => A = lastUnsafe * type WithString = Chunk.Chunk.With // Chunk.NonEmptyChunk * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace Chunk { @@ -1680,6 +1723,7 @@ export declare namespace Chunk { * Chunk.toArray(Chunk.map(Chunk.make(1, 2), (n) => n + 1)) // => [2, 3] * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1719,6 +1763,7 @@ export const map: { * Chunk.toArray(indexed) // => ["0: hello", "1: world", "2: effect"] * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -1750,6 +1795,7 @@ export const mapAccum: { * Chunk.toArray(fails) // => ["negative:-2"] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1805,6 +1851,7 @@ export const partition: { * Chunk.toArray(noErrors) // => [] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1825,6 +1872,7 @@ export const separate = (self: Chunk>): [Chunk, Chunk] * Chunk.size(Chunk.make(1, 2, 3)) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1849,6 +1897,7 @@ export const size = (self: Chunk): number => self.length * Chunk.toArray(Chunk.sort(words, Order.String)) // => ["apple", "banana", "cherry"] * ``` * + * @stability stable * @category sorting * @since 2.0.0 */ @@ -1887,6 +1936,7 @@ export const sort: { * Chunk.toArray(Chunk.sortWith(words, (word) => word.length, Order.Number)) // => ["a", "ab", "abc"] * ``` * + * @stability stable * @category sorting * @since 2.0.0 */ @@ -1922,6 +1972,7 @@ export const sortWith: { * Chunk.toArray(empty2) // => [] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1959,6 +2010,7 @@ export const splitAt: { * // while the second part may be empty * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2000,6 +2052,7 @@ export const splitNonEmptyAt: { * Chunk.toArray(chunks3).map(Chunk.toArray) // => [[1, 2, 3, 4, 5, 6, 7, 8, 9]] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2033,6 +2086,7 @@ export const split: { * Chunk.toArray(allFromFirst) // => [1, 2, 3, 4, 5, 6] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2068,6 +2122,7 @@ export const splitWhere: { * Chunk.tail(Chunk.empty()) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -2091,6 +2146,7 @@ export const tail = (self: Chunk): O.Option> => self.length > 0 ? * // Chunk.tailNonEmpty(Chunk.empty()) // TypeScript error * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -2115,6 +2171,7 @@ export const tailNonEmpty = (self: NonEmptyChunk): Chunk => drop(self, * Chunk.toArray(Chunk.takeRight(chunk, 0)) // => [] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -2145,6 +2202,7 @@ export const takeRight: { * Chunk.toArray(Chunk.takeWhile(small, (n) => n < 10)) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -2183,6 +2241,7 @@ export const takeWhile: { * Chunk.toArray(Chunk.union(withDupes1, withDupes2)) // => [1, 2, 3] * ``` * + * @stability stable * @category set operations * @since 2.0.0 */ @@ -2215,6 +2274,7 @@ export const union: { * Chunk.toArray(Chunk.dedupe(unique)) // => [1, 2, 3] * ``` * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -2236,6 +2296,7 @@ export const dedupe = (self: Chunk): Chunk => fromArrayUnsafe(RA.dedupe * Chunk.toArray(Chunk.dedupeAdjacent(mixed)) // => ["a", "b", "a"] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -2269,6 +2330,7 @@ export const dedupeAdjacent = (self: Chunk): Chunk => fromArrayUnsafe(R * Chunk.toArray(emptyStrs) // => [] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -2295,6 +2357,7 @@ export const unzip = (self: Chunk): [Chunk, Chunk] * Chunk.toArray(Chunk.zipWith(short, long, (n, l) => [n, l])) // => [[1, "a"], [2, "b"]] * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2325,6 +2388,7 @@ export const zipWith: { * Chunk.toArray(Chunk.zip(short, long)) // => [[1, "a"], [2, "b"]] * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2354,6 +2418,7 @@ export const zip: { * Chunk.toArray(Chunk.remove(chunk, 10)) // => ["a", "b", "c", "d"] * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2384,6 +2449,7 @@ export const remove: { * chunk.pipe(Chunk.modify(-1, (n) => n * 10)) // => Option.none() * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2415,6 +2481,7 @@ export const modify: { * chunk.pipe(Chunk.replace(-1, "Z")) // => Option.none() * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -2439,6 +2506,7 @@ export const replace: { * Chunk.toArray(Chunk.makeBy(5, (i) => i * 2)) // => [0, 2, 4, 6, 8] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2464,6 +2532,7 @@ export const makeBy: { * Chunk.toArray(Chunk.range(1, 5)) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2495,6 +2564,7 @@ export const range = (start: number, end: number): NonEmptyChunk => * Chunk.contains(Chunk.empty(), 1) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2529,6 +2599,7 @@ export const contains: { * containsCaseInsensitive(words, "grape") // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2563,6 +2634,7 @@ export const containsWith: ( * firstString // => Option.some("hello") * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -2591,6 +2663,7 @@ export const findFirst: { * Chunk.findFirstIndex(chunk, (n) => n % 2 === 0) // => Option.some(1) * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -2620,6 +2693,7 @@ export const findFirstIndex: { * Chunk.findLast(chunk, (n) => n % 2 === 0) // => Option.some(4) * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -2648,6 +2722,7 @@ export const findLast: { * Chunk.findLastIndex(chunk, (n) => n % 2 === 0) // => Option.some(3) * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -2681,6 +2756,7 @@ export const findLastIndex: { * } * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -2722,6 +2798,7 @@ export const every: { * Chunk.some(words, (word) => word.includes("ban")) // => true * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -2762,6 +2839,7 @@ export const some: { * Chunk.join(Chunk.make("hello"), ", ") // => "hello" * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -2789,6 +2867,7 @@ export const join: { * Chunk.reduce(chunk, -Infinity, (acc, n) => Math.max(acc, n)) // => 5 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -2820,6 +2899,7 @@ export const reduce: { * Chunk.reduceRight(chunk, 0, (acc, n) => n - acc) // => -2 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -2855,6 +2935,7 @@ export const reduceRight: { * Chunk.toArray(caseInsensitive(words1, words2)) // => ["Banana", "Cherry"] * ``` * + * @stability stable * @category filtering * @since 3.2.0 */ @@ -2890,6 +2971,7 @@ export const differenceWith = (isEquivalent: (self: A, that: A) => boolean): * Chunk.toArray(Chunk.difference(chunk1, Chunk.empty())) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category filtering * @since 3.2.0 */ diff --git a/repos/effect/packages/effect/src/Clock.ts b/packages/effect/src/Clock.ts similarity index 98% rename from repos/effect/packages/effect/src/Clock.ts rename to packages/effect/src/Clock.ts index 5434197be..fa04210c7 100644 --- a/repos/effect/packages/effect/src/Clock.ts +++ b/packages/effect/src/Clock.ts @@ -5,6 +5,7 @@ * accessed through a service, tests can replace the clock with a controlled * implementation. * + * @stability stable * @since 2.0.0 */ import type * as Context from "./Context.ts" @@ -45,6 +46,7 @@ import * as effect from "./internal/effect.ts" * await Effect.runPromise(Effect.provideService(clockOperations, Clock.Clock, testClock)) // => [1_000, 1_000_000_000n] * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -183,6 +185,7 @@ export interface Clock { * @see {@link currentTimeMillis} for reading the current time in milliseconds * @see {@link currentTimeNanos} for reading the current time in nanoseconds * + * @stability stable * @category services * @since 2.0.0 */ @@ -219,6 +222,7 @@ export const Clock: Context.Reference = effect.ClockRef * @see {@link Clock} for the service reference * @see {@link currentTimeMillis} for convenience accessor that returns milliseconds * @see {@link currentTimeNanos} for convenience accessor that returns nanoseconds + * @stability stable * @category accessors * @since 2.0.0 */ @@ -259,6 +263,7 @@ export const clockWith: (f: (clock: Clock) => Effect) => Effec * @see {@link monotonicTimeNanos} for measuring elapsed time * @see {@link clockWith} for accessing the full Clock service * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -299,6 +304,7 @@ export const currentTimeMillis: Effect = effect.currentTimeMillis * * @see {@link monotonicTimeNanos} for measuring elapsed time * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -320,6 +326,7 @@ export const currentTimeNanos: Effect = effect.currentTimeNanos * * @see {@link currentTimeNanos} for Unix wall-clock timestamps * + * @stability stable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Combiner.ts b/packages/effect/src/Combiner.ts similarity index 97% rename from repos/effect/packages/effect/src/Combiner.ts rename to packages/effect/src/Combiner.ts index 75ab2d492..d46461421 100644 --- a/repos/effect/packages/effect/src/Combiner.ts +++ b/packages/effect/src/Combiner.ts @@ -7,6 +7,7 @@ * `Combiner` interface, a constructor for custom combining logic, and common * combiners for choosing or ordering values. * + * @stability stable * @since 4.0.0 */ import type * as Order from "./Order.ts" @@ -35,6 +36,7 @@ import type * as Order from "./Order.ts" * ``` * * @see {@link make} – create a `Combiner` from a function + * @stability stable * @category models * @since 4.0.0 */ @@ -73,6 +75,7 @@ export interface Combiner { * ``` * * @see {@link Combiner} – the interface this creates + * @stability stable * @category constructors * @since 4.0.0 */ @@ -104,6 +107,7 @@ export function make(combine: (self: A, that: A) => A): Combiner { * ``` * * @see {@link make} + * @stability stable * @category combinators * @since 4.0.0 */ @@ -137,6 +141,7 @@ export function flip(combiner: Combiner): Combiner { * ``` * * @see {@link max} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -170,6 +175,7 @@ export function min(order: Order.Order): Combiner { * ``` * * @see {@link min} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -199,6 +205,7 @@ export function max(order: Order.Order): Combiner { * ``` * * @see {@link last} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -228,6 +235,7 @@ export function first(): Combiner { * ``` * * @see {@link first} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -260,6 +268,7 @@ export function last(): Combiner { * * @see {@link first} * @see {@link last} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -294,6 +303,7 @@ export function constant(a: A): Combiner { * ``` * * @see {@link make} + * @stability stable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Config.ts b/packages/effect/src/Config.ts similarity index 95% rename from repos/effect/packages/effect/src/Config.ts rename to packages/effect/src/Config.ts index 5690a6398..ba1819372 100644 --- a/repos/effect/packages/effect/src/Config.ts +++ b/packages/effect/src/Config.ts @@ -5,8 +5,10 @@ * multiple settings. Configs are also Effects, so they can be yielded in * `Effect.gen` after a provider has been supplied. * + * @stability stable * @since 4.0.0 */ +import * as Cause from "./Cause.ts" import type { Path, SourceError } from "./ConfigProvider.ts" import * as ConfigProvider from "./ConfigProvider.ts" import * as Effect from "./Effect.ts" @@ -44,6 +46,7 @@ const TypeId = "~effect/Config" * Config.isConfig("not a config") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -66,6 +69,7 @@ export const isConfig = (u: unknown): u is Config => Predicate.hasPrope * @see {@link orElse} – recover from a ConfigError * @see {@link withDefault} – provide a fallback when relevant input is absent * + * @stability stable * @category errors * @since 4.0.0 */ @@ -102,6 +106,7 @@ export class ConfigError { * * @see {@link schema} – the main way to create a Config * + * @stability stable * @category models * @since 2.0.0 */ @@ -177,6 +182,7 @@ const evaluateAt = ( * * @see {@link mapEffect} – when the transformation can fail * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -244,6 +250,7 @@ export const map: { * ) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -284,6 +291,7 @@ export const flatMap: { * * @see {@link map} – when the transformation cannot fail * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -320,7 +328,8 @@ export const mapEffect: { * * The fallback's result replaces the original failure. If the fallback is * absent, an outer {@link withDefault} or {@link option} can recover it. If the - * fallback fails, only its error propagates. + * fallback fails, only its error propagates. Defects and interruptions never + * reach the fallback; they propagate unchanged. * * **Example** (Trying another port before using a default) * @@ -340,6 +349,7 @@ export const mapEffect: { * * @see {@link withDefault} – fallback only on semantic absence * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -348,8 +358,14 @@ export const orElse: { (self: Config, that: (error: ConfigError) => Config): Config } = dual(2, (self: Config, that: (error: ConfigError) => Config): Config => { return make((provider, pathPrefix) => - Effect.matchEffect(evaluateAt(self, provider, pathPrefix), { - onFailure: (error) => evaluateAt(that(error), provider, pathPrefix), + Effect.matchCauseEffect(evaluateAt(self, provider, pathPrefix), { + // Only plain failures reach the fallback; defects and interruptions propagate unchanged. + onFailure: (cause) => { + const error = Cause.findErrorOption(cause) + return Option.isSome(error) && cause.reasons.every(Cause.isFailReason) + ? evaluateAt(that(error.value), provider, pathPrefix) + : Effect.failCause(cause) + }, onSuccess: (resolution): Effect.Effect, ConfigError> => Result.isFailure(resolution) ? evaluateAt(that(resolution.failure), provider, pathPrefix) @@ -397,6 +413,7 @@ export const orElse: { * await Effect.runPromise(dbConfig.parse(missingPort)) // => { host: "localhost", port: 5432 } * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -462,6 +479,7 @@ export function all> | Record(self: Config): Config> => * @see {@link Config} for the config type whose parsed value is extracted * @see {@link Effect.Success} for extracting the success type from any `Effect` * + * @stability stable * @category utility types * @since 2.5.0 */ @@ -544,6 +564,7 @@ export type Success = [T] extends [Config] ? A : never * * @see {@link unwrap} – construct a `Config` from a `Wrap` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -589,6 +610,7 @@ type IsPlainObject = [A] extends [Record] * * @see {@link Wrap} – the utility type accepted by this function * + * @stability stable * @category converting * @since 2.0.0 */ @@ -612,12 +634,22 @@ const isSourceError = (u: unknown): u is ConfigProvider.SourceError => Predicate const cursorToString = (): string => "" +// Rewrites each reason of a failure individually, so unrelated defects and +// interruptions survive the conversion. +const mapReasons = + (f: (reason: Cause.Reason) => Cause.Reason) => + (self: Effect.Effect): Effect.Effect => + Effect.catchCause(self, (cause) => Effect.failCause(Cause.fromReasons(cause.reasons.map(f)))) + const loadCursor: ( provider: ConfigProvider.ConfigProvider, path: Path ) => Effect.Effect = (provider, path) => provider.load(path).pipe( - Effect.orDie, + // Source errors travel as defects so they bypass schema issue recovery; `schema` converts them back. + mapReasons((reason) => + Cause.isFailReason(reason) ? Cause.makeDieReason(reason.error).annotate(Cause.reasonAnnotations(reason)) : reason + ), Effect.mapEager((node) => ({ provider, path, node, toString: cursorToString })) ) @@ -810,6 +842,7 @@ const toConfigCursorAST = memoize((root: SchemaAST.AST): SchemaAST.AST => { * @see {@link String} / {@link Number} / {@link Boolean} – shortcuts for * single-value configs * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -836,7 +869,11 @@ export function schema(codec: Schema.ConstraintCodec, path?: stri }) ) ), - Effect.catchDefect((defect) => isSourceError(defect) ? Effect.fail(new ConfigError(defect)) : Effect.die(defect)) + mapReasons((reason) => + Cause.isDieReason(reason) && isSourceError(reason.defect) + ? Cause.makeFailReason(new ConfigError(reason.defect)).annotate(Cause.reasonAnnotations(reason)) + : reason + ) ) }) } @@ -868,6 +905,7 @@ const isPath = (u: unknown): u is string | Path => Predicate.isString(u) || glob * Use when you need to re-raise a specific config error, such as inside * {@link orElse}. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -896,6 +934,7 @@ export function fail(err: SourceError | Schema.SchemaError): Config { * Effect.runSync(host.parse(provider)) // => "localhost" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -928,6 +967,7 @@ export function succeed(value: T) { * @see {@link NonEmptyString} – rejects empty strings * @see {@link schema} – for more complex types * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -949,6 +989,7 @@ export function String(name?: string) { * * @see {@link String} for allowing empty strings * + * @stability stable * @category constructors * @since 3.7.0 */ @@ -971,6 +1012,7 @@ export function NonEmptyString(name?: string) { * @see {@link Finite} for rejecting `NaN` and `Infinity` * @see {@link Int} for accepting only integers * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -992,6 +1034,7 @@ export function Number(name?: string) { * @see {@link Number} for accepting `NaN` and `Infinity` * @see {@link Int} for accepting only integers * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1013,6 +1056,7 @@ export function Finite(name?: string) { * @see {@link Number} for accepting any number * @see {@link Port} for accepting only integers in `1` through `65535` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1042,6 +1086,7 @@ export function Int(name?: string) { * ``` * * @see {@link Literals} – accepts multiple literal values + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1072,6 +1117,7 @@ export function Literal(literal: L, name?: str * * @see {@link Literal} for accepting one specific literal value * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1105,6 +1151,7 @@ export function Literals>( * ``` * * @see {@link Record} for key-value input from structural records or separated strings. + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1172,6 +1219,7 @@ export function Array>( * ``` * * @see {@link Array} for array input from structural arrays or separated strings. + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1247,6 +1295,7 @@ export function Record< * ) // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1289,6 +1338,7 @@ export function Boolean(name?: string) { * * @see {@link schema} for decoding configuration values with a custom codec * + * @stability stable * @category constructors * @since 2.5.0 */ @@ -1304,6 +1354,7 @@ export function Duration(name?: string) { * Decimal symbols such as `kB` use powers of 1,000, while binary symbols such * as `KiB` use powers of 1,024. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1342,6 +1393,7 @@ export function ByteSize(name?: string) { * * @see {@link Int} for integer config values outside the port range * + * @stability stable * @category constructors * @since 3.16.0 */ @@ -1379,6 +1431,7 @@ export function Port(name?: string) { * ) // => "Info" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1419,6 +1472,7 @@ export function LogLevel(name?: string) { * * @see {@link String} for non-secret string settings * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1461,6 +1515,7 @@ export function Redacted(name?: string) { * * @see {@link schema} for decoding configuration values with a custom codec * + * @stability stable * @category constructors * @since 3.11.0 */ @@ -1494,6 +1549,7 @@ export function URL(name?: string) { * Effect.runSync(createdAt.parse(provider)).toISOString() // => "2024-01-15T00:00:00.000Z" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1549,6 +1605,7 @@ export function Date(name?: string) { * @see {@link all} – combine multiple configs into a struct * @see {@link schema} – read structured config from a schema * + * @stability stable * @category combinators * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/ConfigProvider.ts b/packages/effect/src/ConfigProvider.ts similarity index 98% rename from repos/effect/packages/effect/src/ConfigProvider.ts rename to packages/effect/src/ConfigProvider.ts index 59d4e6f87..4250c4bc2 100644 --- a/repos/effect/packages/effect/src/ConfigProvider.ts +++ b/packages/effect/src/ConfigProvider.ts @@ -6,6 +6,7 @@ * for composing providers, changing paths, and installing providers through * layers. * + * @stability stable * @since 4.0.0 */ @@ -50,6 +51,7 @@ import * as Str from "./String.ts" * @see {@link makeRecord} – construct a `Record` node * @see {@link makeArray} – construct an `Array` node * + * @stability stable * @category models * @since 4.0.0 */ @@ -95,6 +97,7 @@ export type Node = * @see {@link makeRecord} – for object-like containers * @see {@link makeArray} – for array-like containers * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -133,6 +136,7 @@ export function makeValue(value: string): Node { * @see {@link makeValue} – for terminal leaves * @see {@link makeArray} – for array-like containers * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -165,6 +169,7 @@ export function makeRecord(keys: ReadonlySet, value?: string): Node { * @see {@link makeValue} – for terminal leaves * @see {@link makeRecord} – for object-like containers * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -202,6 +207,7 @@ export function makeArray(length: number, value?: string): Node { * @see {@link ConfigProvider} – the interface whose `load` may fail with this * error * + * @stability stable * @category errors * @since 4.0.0 */ @@ -229,6 +235,7 @@ export class SourceError extends Data.TaggedError("SourceError")<{ * path.join(".") // => "database.replicas.0.host" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -264,6 +271,7 @@ export type Path = ReadonlyArray * @see {@link make} – construct a provider from a lookup function * @see {@link orElse} – compose providers with fallback * + * @stability stable * @category services * @since 4.0.0 */ @@ -336,6 +344,7 @@ export interface ConfigProvider extends Pipeable { * @see {@link layer} – install a provider as a Layer * @see {@link layerAdd} – add a fallback provider as a Layer * + * @stability stable * @category services * @since 4.0.0 */ @@ -429,6 +438,7 @@ function makeOrElse(first: ConfigProvider, second: ConfigProvider): ConfigProvid * @see {@link fromEnv} – pre-built provider for environment variables * @see {@link fromUnknown} – pre-built provider for JSON objects * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -475,6 +485,7 @@ export function make(get: (path: Path) => Effect.Effect ConfigProvider = mapInput(( * * @see {@link mapInput} – for arbitrary path transformations * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -661,6 +675,7 @@ export const nested: { * * @see {@link layerAdd} – add a provider without replacing the existing one * + * @stability stable * @category layers * @since 4.0.0 */ @@ -707,6 +722,7 @@ export const layer = ( * @see {@link layer} – replace the provider entirely * @see {@link orElse} – compose providers without layers * + * @stability stable * @category layers * @since 4.0.0 */ @@ -774,6 +790,7 @@ export const layerAdd = ( * @see {@link fromEnv} – for environment variables * @see {@link make} – for custom backing stores * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -858,6 +875,7 @@ function emptyStringAsMissing(value: string | undefined, preserveEmptyStrings: b * * @see {@link fromEnv} – automatically reads the runtime environment * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -920,6 +938,7 @@ export function fromEnvRecord( * @see {@link fromEnvRecord} – for explicit records in restricted runtimes * @see {@link constantCase} – bridge camelCase keys to SCREAMING_SNAKE_CASE * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1039,6 +1058,7 @@ function trieNodeAt(root: EnvTrieNode, path: Path): EnvTrieNode | undefined { * @see {@link fromEnvRecord} – for explicit environment records * @see {@link fromEnv} – for raw environment variable access * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1194,6 +1214,7 @@ function searchLast(str: string, rgx: RegExp): number { * @see {@link fromDotEnvContents} – parse a `.env` string directly * @see {@link fromEnv} – read from the runtime environment * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1265,6 +1286,7 @@ export const fromDotEnv: (options?: { * @see {@link fromEnv} – for environment variables * @see {@link fromDotEnv} – for `.env` files * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Console.ts b/packages/effect/src/Console.ts similarity index 98% rename from repos/effect/packages/effect/src/Console.ts rename to packages/effect/src/Console.ts index 83264a74b..f00448c5b 100644 --- a/repos/effect/packages/effect/src/Console.ts +++ b/packages/effect/src/Console.ts @@ -7,6 +7,7 @@ * tests or other environments. This module also includes scoped helpers that * close console groups or timers automatically. * + * @stability stable * @since 2.0.0 */ import type * as Context from "./Context.ts" @@ -19,6 +20,7 @@ import type { Scope } from "./Scope.ts" /** * Represents a console interface for logging, debugging, timing, and grouping output. * + * @stability stable * @category services * @since 2.0.0 */ @@ -77,6 +79,7 @@ export interface Console { * * @see {@link consoleWith} for using the current console service inside an effect * + * @stability stable * @category services * @since 2.0.0 */ @@ -106,6 +109,7 @@ export const Console: Context.Reference = effect.ConsoleRef * messages // => ["Hello, world!", "This is an error message"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -135,6 +139,7 @@ export const consoleWith = (f: (console: Console) => Effect.Effect ["This will be logged as an error"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -178,6 +183,7 @@ export const assert = (condition: boolean, ...args: ReadonlyArray): Effect. * operations // => ["log:This will be cleared", "clear", "log:This appears after clearing"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -214,6 +220,7 @@ export const clear: Effect.Effect = consoleWith((console) => * messages // => ["my-counter: 1", "my-counter: 2", "default: 1"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -253,6 +260,7 @@ export const count = (label?: string): Effect.Effect => * messages // => ["my-counter: 1", "my-counter: 2", "my-counter: 1"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -290,6 +298,7 @@ export const countReset = (label?: string): Effect.Effect => * messages // => [["Debug info:", { userId: 123, action: "login" }], ["Processing step", 1, "of", 5]] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -326,6 +335,7 @@ export const debug = (...args: ReadonlyArray): Effect.Effect => * inspected // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -356,6 +366,7 @@ export const dir = (item: any, options?: any): Effect.Effect => * messages // => ["Ada"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -395,6 +406,7 @@ export const dirxml = (...args: ReadonlyArray): Effect.Effect => * messages // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -441,6 +453,7 @@ export const error = (...args: ReadonlyArray): Effect.Effect => * operations // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -492,6 +505,7 @@ export const group = ( * messages // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -529,6 +543,7 @@ export const info = (...args: ReadonlyArray): Effect.Effect => * messages // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -568,6 +583,7 @@ export const log = (...args: ReadonlyArray): Effect.Effect => * calls // => [{ rows: 3, properties: undefined }, { rows: 3, properties: ["name", "age"] }] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -607,6 +623,7 @@ export const table = (tabularData: any, properties?: ReadonlyArray): Eff * operations // => ["start:operation-timer", "log:Operation completed", "end:operation-timer"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -652,6 +669,7 @@ export const time = (label?: string | undefined): Effect.Effect [["start", "long-operation"], ["log", "long-operation", "Halfway done"], ["end", "long-operation"]] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -685,6 +703,7 @@ export const timeLog = (label?: string, ...args: ReadonlyArray): Effect.Eff * traces // => [["Debug trace point"], ["Function call:", { functionName: "processData" }]] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -723,6 +742,7 @@ export const trace = (...args: ReadonlyArray): Effect.Effect => * messages // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -769,6 +789,7 @@ export const warn = (...args: ReadonlyArray): Effect.Effect => * operations // => expected * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -832,6 +853,7 @@ export const withGroup = dual< * operations // => ["start:my-operation", "log:Operation completed", "end:my-operation"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Context.ts b/packages/effect/src/Context.ts similarity index 98% rename from repos/effect/packages/effect/src/Context.ts rename to packages/effect/src/Context.ts index a3d918ff8..90e6f0d6c 100644 --- a/repos/effect/packages/effect/src/Context.ts +++ b/packages/effect/src/Context.ts @@ -8,6 +8,7 @@ * for creating keys, building contexts, adding and reading services, merging * contexts, and selecting or removing services. * + * @stability stable * @since 4.0.0 */ import type { Effect, EffectIterator } from "./Effect.ts" @@ -26,6 +27,7 @@ import type * as Types from "./Types.ts" * String literal type used as the runtime type identifier for `Context` * service keys. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -35,6 +37,7 @@ export type ServiceTypeId = "~effect/Context/Service" * Runtime type identifier attached to `Context` service keys and used by * `isKey` to recognize them. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -58,6 +61,7 @@ export const ServiceTypeId: ServiceTypeId = "~effect/Context/Service" * @see {@link Service} for creating required service keys * @see {@link Reference} for creating service keys with default values * + * @stability stable * @category models * @since 4.0.0 */ @@ -92,6 +96,7 @@ export interface Key extends Effect "Result: SELECT 1" * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -117,6 +122,7 @@ export interface Service extends Key ["default logger"] * ``` * + * @stability stable * @category services * @since 3.11.0 */ @@ -360,6 +369,7 @@ export interface Reference extends Service { * Database.key // => "Database" * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace Service { @@ -461,6 +471,7 @@ const TypeId = "~effect/Context" as const * Context.get(context, Database).query("SELECT 1") // => "Result: SELECT 1" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -576,6 +587,7 @@ const lookup = (self: Context, key: string): unknown => { * context.mapUnsafe.size // => 1 * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -648,6 +660,7 @@ export const hasSameCache = ( * @see {@link isKey} for checking service keys * @see {@link isReference} for checking references with defaults * + * @stability stable * @category guards * @since 2.0.0 */ @@ -663,6 +676,7 @@ export const isContext = (u: unknown): u is Context => hasProperty(u, Typ * Context.isKey(Context.Service("Service")) // => true * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -684,6 +698,7 @@ export const isKey = (u: unknown): u is Key => hasProperty(u, ServiceT * Context.isReference(Context.Service("Key")) // => false * ``` * + * @stability stable * @category guards * @since 3.11.0 */ @@ -699,6 +714,7 @@ export const isReference = (u: Key): u is Reference => !!(u as Re * Context.empty().mapUnsafe.size // => 0 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -720,6 +736,7 @@ const emptyContext = makeUnsafe(new Map()) * Context.get(context, Port).PORT // => 8080 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -761,6 +778,7 @@ export const make = ( * * @see {@link addOrOmit} for adding or removing a service from an `Option` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -783,6 +801,7 @@ export const add: { /** * Adds a service by key to a given `Context` using a string key. * + * @stability stable * @category combining * @since 4.0.0 */ @@ -841,6 +860,7 @@ export const addUnsafe = ( * * @see {@link add} for always storing a service value * + * @stability stable * @category combining * @since 4.0.0 */ @@ -907,6 +927,7 @@ export const addOrOmit: { * * @see {@link getOption} for returning `Option.none` when a non-reference key is missing * + * @stability stable * @category getters * @since 3.7.0 */ @@ -935,6 +956,7 @@ export const getOrElse: { * * @see {@link getOption} for a reference-aware optional lookup * + * @stability stable * @category getters * @since 4.0.0 */ @@ -984,6 +1006,7 @@ export const getOrUndefinedUnsafe = (self: Context) => { * * @see {@link getOrElse} for returning a fallback value directly * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1132,6 +1157,7 @@ export const getOption: { * * @see {@link mergeAll} for merging more than two contexts at once * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1180,6 +1206,7 @@ export const merge: { * * @see {@link merge} for merging two contexts * + * @stability stable * @category combining * @since 3.12.0 */ @@ -1223,6 +1250,7 @@ export const mergeAll = >( * * @see {@link omit} for removing selected services * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1265,6 +1293,7 @@ export const pick = >>( * * @see {@link pick} for keeping selected services * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1321,6 +1350,7 @@ export const omit = >>( * * @see {@link Service} for required services without default values * + * @stability stable * @category services * @since 3.11.0 */ diff --git a/repos/effect/packages/effect/src/Cron.ts b/packages/effect/src/Cron.ts similarity index 97% rename from repos/effect/packages/effect/src/Cron.ts rename to packages/effect/src/Cron.ts index 9d921ce56..477667a2a 100644 --- a/repos/effect/packages/effect/src/Cron.ts +++ b/packages/effect/src/Cron.ts @@ -5,6 +5,7 @@ * can create or parse schedules, compare them, test whether a date matches, and * find previous or next scheduled occurrences. * + * @stability stable * @since 2.0.0 */ import * as Arr from "./Array.ts" @@ -13,7 +14,7 @@ import type * as DateTime from "./DateTime.ts" import * as Equal from "./Equal.ts" import * as Equ from "./Equivalence.ts" import { format as formatValue } from "./Formatter.ts" -import { constVoid, dual, pipe } from "./Function.ts" +import { constFalse, constVoid, dual, pipe } from "./Function.ts" import * as Hash from "./Hash.ts" import { type Inspectable, NodeInspectSymbol } from "./Inspectable.ts" import * as dateTime from "./internal/dateTime.ts" @@ -65,6 +66,7 @@ const TypeId = "~effect/Cron" * @see {@link match} for testing a date against a schedule * @see {@link next} for finding the next scheduled occurrence * + * @stability stable * @category models * @since 2.0.0 */ @@ -201,6 +203,7 @@ const CronProto = { * @see {@link make} for constructing a `Cron` value directly * @see {@link parse} for constructing a `Cron` value from a string * + * @stability stable * @category guards * @since 2.0.0 */ @@ -288,6 +291,7 @@ export const isCron = (u: unknown): u is Cron => hasProperty(u, TypeId) * * @see {@link parse} for building a schedule from a cron expression string * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -465,6 +469,7 @@ const CronParseErrorTypeId = "~effect/Cron/CronParseError" * @see {@link parse} for the parser that returns this error in `Result.fail` * @see {@link isCronParseError} for narrowing unknown values to this error type * + * @stability stable * @category errors * @since 4.0.0 */ @@ -501,6 +506,7 @@ export class CronParseError extends Data.TaggedError("CronParseError")<{ * @see {@link CronParseError} for the parse error type * @see {@link parse} for producing `CronParseError` values on invalid input * + * @stability stable * @category guards * @since 4.0.0 */ @@ -539,6 +545,7 @@ export const isCronParseError = (u: unknown): u is CronParseError => hasProperty * @see {@link parseUnsafe} for throwing on invalid cron expressions * @see {@link make} for constructing a schedule from explicit field constraints * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -607,6 +614,7 @@ export const parse = (cron: string, tz?: DateTime.TimeZone | string): Result.Res * Cron.match(cronWithTz, "2024-01-01T14:00:00Z") // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -637,6 +645,7 @@ export const parseUnsafe = (cron: string, tz?: DateTime.TimeZone | string): Cron * Cron.format(cron, { includeSeconds: true }) // => "0 23 0-20/2 * * 0" * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -708,6 +717,7 @@ const formatSegment = (values: ReadonlySet): string => { * @see {@link next} for finding the next matching date/time * @see {@link prev} for finding the previous matching date/time * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -783,6 +793,7 @@ const daysInMonth = (date: Date): number => * @see {@link prev} for finding the previous scheduled occurrence * @see {@link sequence} for iterating future scheduled occurrences * + * @stability stable * @category getters * @since 2.0.0 */ @@ -809,6 +820,7 @@ export const next = (cron: Cron, now?: DateTime.DateTime.Input): Date => { * * @see {@link next} for finding the next scheduled occurrence * + * @stability stable * @category getters * @since 3.20.0 */ @@ -845,6 +857,14 @@ const stepCron = (cron: Cron, now: DateTime.DateTime.Input | undefined, directio } } + // Repeated wall times resolve to their first occurrence, so when the search + // starts in the second occurrence, matches inside that fold are in the past. + const isBeforeStart = reverse || utc ? constFalse : (current: Date) => + dateTime.makeZonedUnsafe(current, { + timeZone: zoned.zone, + adjustForTimeZone: true + }).epochMilliseconds <= zoned.epochMilliseconds + const result = dateTime.mutate(zoned, (current) => { current.setUTCSeconds(current.getUTCSeconds() + tick, 0) @@ -988,6 +1008,12 @@ const stepCron = (cron: Cron, now: DateTime.DateTime.Input | undefined, directio } } + if (isBeforeStart(current)) { + current.setUTCSeconds(current.getUTCSeconds() + 1) + i = -1 // skipped fold candidates do not count against the search budget + continue + } + return } @@ -1033,6 +1059,7 @@ const stepCron = (cron: Cron, now: DateTime.DateTime.Input | undefined, directio * * @see {@link next} for computing one next occurrence * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1082,6 +1109,7 @@ export const sequence = function*(cron: Cron, now?: DateTime.DateTime.Input): It * * @see {@link equals} for directly comparing two `Cron` values * + * @stability stable * @category instances * @since 2.0.0 */ @@ -1141,6 +1169,7 @@ const restrictionsEquals = (self: ReadonlySet, that: ReadonlySet * * @see {@link Equivalence} for the reusable equivalence instance * + * @stability stable * @category predicates * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Crypto.ts b/packages/effect/src/Crypto.ts similarity index 98% rename from repos/effect/packages/effect/src/Crypto.ts rename to packages/effect/src/Crypto.ts index fa16326e0..43daf4330 100644 --- a/repos/effect/packages/effect/src/Crypto.ts +++ b/packages/effect/src/Crypto.ts @@ -7,6 +7,7 @@ * secure random bytes and numbers, UUIDv4 and UUIDv7 generation, shuffling, and * SHA message digests. * + * @stability unstable * @since 4.0.0 */ import * as Context from "./Context.ts" @@ -34,6 +35,7 @@ const TypeId = "~effect/Crypto" * const algorithm: Crypto.DigestAlgorithm = "SHA-256" * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -72,6 +74,7 @@ export type DigestAlgorithm = "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512" * await Effect.runPromise(Effect.provide(program, TestCrypto)) // => [16, 36, 16] * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -185,6 +188,7 @@ export interface Crypto { * * @see {@link make} for constructing a Crypto service from primitive operations * + * @stability unstable * @category services * @since 4.0.0 */ @@ -224,6 +228,7 @@ export const Crypto: Context.Service = Context.Service("effect/C * await Effect.runPromise(testCrypto.randomBytes(4)) // => new Uint8Array([0, 0, 0, 0]) * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Data.ts b/packages/effect/src/Data.ts similarity index 99% rename from repos/effect/packages/effect/src/Data.ts rename to packages/effect/src/Data.ts index 2228bb0e9..c0e3085a8 100644 --- a/repos/effect/packages/effect/src/Data.ts +++ b/packages/effect/src/Data.ts @@ -6,6 +6,7 @@ * makes them easy to narrow with pattern matching or simple checks. These * helpers are commonly used for domain values and errors in Effect programs. * + * @stability stable * @since 2.0.0 */ import type * as Cause from "./Cause.ts" @@ -42,6 +43,7 @@ import type { Unify } from "./Unify.ts" * @see {@link TaggedClass} — adds a `_tag` field * @see {@link Error} — yieldable error variant * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -85,6 +87,7 @@ export const Class: new = {}>( * @see {@link TaggedError} — tagged error variant * @see {@link TaggedEnum} — multi-variant unions * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -136,6 +139,7 @@ export const TaggedClass = ( * @see {@link TaggedEnum.WithGenerics} — generic tagged enums * @see {@link TaggedEnum.Constructor} — the constructor object type * + * @stability stable * @category models * @since 2.0.0 */ @@ -172,6 +176,7 @@ type UntaggedChildren = true extends ChildrenAreTagged * values ({@link TaggedEnum.Value}) * - Full constructor objects ({@link TaggedEnum.Constructor}) * + * @stability stable * @since 2.0.0 */ export declare namespace TaggedEnum { @@ -574,6 +579,7 @@ export declare namespace TaggedEnum { * @see {@link TaggedEnum.Constructor} — the returned object type * @see {@link TaggedEnum.WithGenerics} — generic enum support * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -706,6 +712,7 @@ function taggedMatch< * @see {@link TaggedError} — adds a `_tag` for `Effect.catchTag` * @see {@link Class} — non-error data class * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -755,6 +762,7 @@ export const Error: new = {}>( * @see {@link Error} — without a `_tag` * @see {@link TaggedClass} — tagged class that is not an error * + * @stability stable * @category constructors * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/DateTime.ts b/packages/effect/src/DateTime.ts similarity index 97% rename from repos/effect/packages/effect/src/DateTime.ts rename to packages/effect/src/DateTime.ts index c121861a2..38a7e456a 100644 --- a/repos/effect/packages/effect/src/DateTime.ts +++ b/packages/effect/src/DateTime.ts @@ -8,6 +8,7 @@ * helpers, comparisons, date arithmetic, current-time effects, and formatting * functions. * + * @stability stable * @since 3.6.0 */ import type { IllegalArgumentError } from "./Cause.ts" @@ -31,6 +32,7 @@ const TimeZoneTypeId = Internal.TimeZoneTypeId * A `DateTime` represents a point in time. It can optionally have a time zone * associated with it. * + * @stability stable * @category models * @since 3.6.0 */ @@ -44,6 +46,7 @@ export type DateTime = Utc | Zoned * * Use `DateTime.isUtc` to narrow a `DateTime` to this variant. * + * @stability stable * @category models * @since 3.6.0 */ @@ -62,6 +65,7 @@ export interface Utc extends DateTime.Proto { * `epochMilliseconds`, while the time zone is used for wall-clock parts, * formatting, and zone-aware transformations. * + * @stability stable * @category models * @since 3.6.0 */ @@ -78,6 +82,7 @@ export interface Zoned extends DateTime.Proto { * Companion namespace containing the public helper types used by `DateTime` * constructors, parts APIs, formatting, and date/time arithmetic. * + * @stability stable * @since 3.6.0 */ export declare namespace DateTime { @@ -271,6 +276,7 @@ export declare namespace DateTime { * * A `TimeZone` is either a fixed offset from UTC or a named IANA time zone. * + * @stability stable * @category models * @since 3.6.0 */ @@ -280,6 +286,7 @@ export type TimeZone = TimeZone.Offset | TimeZone.Named * Companion namespace containing the public variant and protocol types for * `TimeZone`. * + * @stability stable * @since 3.6.0 */ export declare namespace TimeZone { @@ -397,6 +404,7 @@ export declare namespace TimeZone { * afterGap.pipe(Option.getOrThrow, DateTime.formatIso) // => "2025-03-09T07:30:00.000Z" * ``` * + * @stability stable * @category models * @since 3.18.0 */ @@ -416,6 +424,7 @@ export type Disambiguation = "compatible" | "earlier" | "later" | "reject" * @see {@link isUtc} for narrowing a known `DateTime` to UTC * @see {@link isZoned} for narrowing a known `DateTime` to zoned * + * @stability stable * @category guards * @since 3.6.0 */ @@ -432,6 +441,7 @@ export const isDateTime: (u: unknown) => u is DateTime = Internal.isDateTime * @see {@link isTimeZoneOffset} for narrowing to fixed-offset time zones * @see {@link isTimeZoneNamed} for narrowing to named time zones * + * @stability stable * @category guards * @since 3.6.0 */ @@ -448,6 +458,7 @@ export const isTimeZone: (u: unknown) => u is TimeZone = Internal.isTimeZone * @see {@link isTimeZone} for checking either time zone variant * @see {@link isTimeZoneNamed} for narrowing to named time zones * + * @stability stable * @category guards * @since 3.6.0 */ @@ -464,6 +475,7 @@ export const isTimeZoneOffset: (u: unknown) => u is TimeZone.Offset = Internal.i * @see {@link isTimeZone} for checking either time zone variant * @see {@link isTimeZoneOffset} for narrowing to fixed-offset time zones * + * @stability stable * @category guards * @since 3.6.0 */ @@ -480,6 +492,7 @@ export const isTimeZoneNamed: (u: unknown) => u is TimeZone.Named = Internal.isT * @see {@link isZoned} for narrowing to zoned date-times * @see {@link match} for handling both UTC and zoned cases * + * @stability stable * @category guards * @since 3.6.0 */ @@ -496,6 +509,7 @@ export const isUtc: (self: DateTime) => self is Utc = Internal.isUtc * @see {@link isUtc} for narrowing to UTC date-times * @see {@link match} for handling both UTC and zoned cases * + * @stability stable * @category guards * @since 3.6.0 */ @@ -526,6 +540,7 @@ export const isZoned: (self: DateTime) => self is Zoned = Internal.isZoned * DateTime.Equivalence(utc, zoned) // => true * ``` * + * @stability stable * @category instances * @since 3.6.0 */ @@ -553,6 +568,7 @@ export const Equivalence: Equ.Equivalence = Internal.Equivalence * Array.sort(dates, DateTime.Order).map(DateTime.formatIsoDateUtc) // => ["2024-01-01", "2024-02-01", "2024-03-01"] * ``` * + * @stability stable * @category instances * @since 3.6.0 */ @@ -579,6 +595,7 @@ export const Order: order.Order = Internal.Order * DateTime.clamp(date, { minimum: min, maximum: max }) // => DateTime.makeUnsafe("2024-12-31") * ``` * + * @stability stable * @category ordering * @since 3.6.0 */ @@ -611,6 +628,7 @@ export const clamp: { * DateTime.fromDateUnsafe(new Date("2024-01-01T12:00:00Z")) // => DateTime.makeUnsafe("2024-01-01T12:00:00Z") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -647,6 +665,7 @@ export const fromDateUnsafe: (date: Date) => Utc = Internal.fromDateUnsafe * DateTime.makeUnsafe("2024-01-01") // => DateTime.makeUnsafe("2024-01-01T00:00:00Z") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -663,6 +682,7 @@ export const makeUnsafe: (input: A) => DateTime.Preser * DateTime.fromEpochSeconds(1704067200).toJSON() // => "2024-01-01T00:00:00.000Z" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -701,6 +721,7 @@ export const fromEpochSeconds: (seconds: number) => Utc = Internal.fromEpochSeco * DateTime.formatIsoZoned(zoned) // => "2024-06-15T15:30:00.000+01:00[Europe/London]" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -743,6 +764,7 @@ export const makeZonedUnsafe: (input: DateTime.Input, options?: { * result.pipe(Option.map(DateTime.formatIsoZoned)) // => Option.some("2024-06-15T15:30:00.000+01:00[Europe/London]") * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -787,6 +809,7 @@ export const makeZoned: ( * DateTime.make("not a date") // => Option.none() * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -815,6 +838,7 @@ export const make: (input: A) => Option.Option Option.none() * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -832,6 +856,7 @@ export const makeZonedFromString: (input: string) => Option.Option = Inte * await Effect.runPromise(Effect.map(DateTime.now, DateTime.isDateTime)) // => true * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -850,6 +875,7 @@ export const now: Effect.Effect = Internal.now * await Effect.runPromise(Effect.map(DateTime.nowAsDate, (now) => now instanceof Date)) // => true * ``` * + * @stability stable * @category constructors * @since 3.14.0 */ @@ -876,6 +902,7 @@ export const nowAsDate: Effect.Effect = Internal.nowAsDate * Number.isFinite(DateTime.toEpochMillis(DateTime.nowUnsafe())) // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -911,6 +938,7 @@ export const nowUnsafe: LazyArg = Internal.nowUnsafe * utc // => DateTime.makeUnsafe("2024-01-01T00:00:00Z") * ``` * + * @stability stable * @category converting * @since 3.13.0 */ @@ -930,6 +958,7 @@ export const toUtc: (self: DateTime) => Utc = Internal.toUtc * DateTime.isZoned(zoned) // => true * ``` * + * @stability stable * @category transforming * @since 3.6.0 */ @@ -962,6 +991,7 @@ export const setZone: { * DateTime.zoneToString(zoned.zone) // => "+03:00" * ``` * + * @stability stable * @category transforming * @since 3.6.0 */ @@ -1000,6 +1030,7 @@ export const setZoneOffset: { * // DateTime.zoneMakeNamedUnsafe("Invalid/Zone") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1027,6 +1058,7 @@ export const zoneMakeNamedUnsafe: (zoneId: string) => TimeZone.Named = Internal. * DateTime.formatIsoZoned(dt) // => "2024-01-01T15:00:00.000+03:00" * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -1048,6 +1080,7 @@ export const zoneMakeOffset: (offset: number) => TimeZone.Offset = Internal.zone * DateTime.zoneMakeNamed("Invalid/Zone") // => Option.none() * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -1075,6 +1108,7 @@ export const zoneMakeNamed: (zoneId: string) => Option.Option = * DateTime.zoneToString((await Effect.runPromise(program)).zone) // => "Europe/London" * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -1097,6 +1131,7 @@ export const zoneMakeNamedEffect: (zoneId: string) => Effect.Effect true * ``` * + * @stability stable * @category constructors * @since 3.6.0 */ @@ -1119,6 +1154,7 @@ export const zoneMakeLocal: () => TimeZone.Named = Internal.zoneMakeLocal * DateTime.zoneFromString("invalid") // => Option.none() * ``` * + * @stability stable * @category decoding * @since 3.6.0 */ @@ -1136,6 +1172,7 @@ export const zoneFromString: (zone: string) => Option.Option = Interna * DateTime.zoneToString(DateTime.zoneMakeNamedUnsafe("Europe/London")) // => "Europe/London" * ``` * + * @stability stable * @category encoding * @since 3.6.0 */ @@ -1156,6 +1193,7 @@ export const zoneToString: (self: TimeZone) => string = Internal.zoneToString * result // => Option.some("2024-01-01T00:00:00.000+00:00[Europe/London]") * ``` * + * @stability stable * @category transforming * @since 3.6.0 */ @@ -1185,6 +1223,7 @@ export const setZoneNamed: { * DateTime.zoneToString(zoned.zone) // => "Europe/London" * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1224,6 +1263,7 @@ export const setZoneNamedUnsafe: { * DateTime.distance(start, end) // => Duration.minutes(1) * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1246,6 +1286,7 @@ export const distance: { * DateTime.min(date1, date2) // => DateTime.makeUnsafe("2024-01-01") * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1268,6 +1309,7 @@ export const min: { * DateTime.max(date1, date2) // => DateTime.makeUnsafe("2024-02-01") * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1291,6 +1333,7 @@ export const max: { * DateTime.isGreaterThan(date2, date1) // => false * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1316,6 +1359,7 @@ export const isGreaterThan: { * DateTime.isGreaterThanOrEqualTo(date1, date3) // => false * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1339,6 +1383,7 @@ export const isGreaterThanOrEqualTo: { * DateTime.isLessThan(date2, date1) // => false * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1364,6 +1409,7 @@ export const isLessThan: { * DateTime.isLessThanOrEqualTo(date3, date1) // => false * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1387,6 +1433,7 @@ export const isLessThanOrEqualTo: { * DateTime.between(date, { minimum: min, maximum: max }) // => true * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1412,6 +1459,7 @@ export const between: { * await Effect.runPromise(Effect.provide(DateTime.isFuture(futureDate), TestClock.layer())) // => true * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1438,6 +1486,7 @@ export const isFuture: (self: DateTime) => Effect.Effect = Internal.isF * DateTime.isFutureUnsafe(oneHourFromNow) * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1460,6 +1509,7 @@ export const isFutureUnsafe: (self: DateTime) => boolean = Internal.isFutureUnsa * await Effect.runPromise(Effect.provide(DateTime.isPast(pastDate), TestClock.layer())) // => true * ``` * + * @stability stable * @category comparisons * @since 3.6.0 */ @@ -1486,6 +1536,7 @@ export const isPast: (self: DateTime) => Effect.Effect = Internal.isPas * DateTime.isPastUnsafe(oneHourAgo) * ``` * + * @stability stable * @category comparisons * @since 4.0.0 */ @@ -1514,6 +1565,7 @@ export const isPastUnsafe: (self: DateTime) => boolean = Internal.isPastUnsafe * DateTime.toDateUtc(dt).toISOString() // => "2024-01-01T12:00:00.000Z" * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1541,6 +1593,7 @@ export const toDateUtc: (self: DateTime) => Date = Internal.toDateUtc * DateTime.toDate(zoned).toISOString() // => "2024-01-01T12:00:00.000Z" * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1566,6 +1619,7 @@ export const toDate: (self: DateTime) => Date = Internal.toDate * DateTime.zonedOffset(zoned) // => 0 * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1590,6 +1644,7 @@ export const zonedOffset: (self: Zoned) => number = Internal.zonedOffset * DateTime.zonedOffsetIso(zoned) // => "+03:00" * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1611,6 +1666,7 @@ export const zonedOffsetIso: (self: Zoned) => string = Internal.zonedOffsetIso * DateTime.toEpochMillis(dt) // => 1704067200000 * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1633,6 +1689,7 @@ export const toEpochMillis: (self: DateTime) => number = Internal.toEpochMillis * DateTime.toEpochSeconds(dt) // => 1704067200 * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1657,6 +1714,7 @@ export const toEpochSeconds: (self: DateTime) => number = Internal.toEpochSecond * ) // => "2024-01-01T00:00:00.000Z" * ``` * + * @stability stable * @category converting * @since 3.6.0 */ @@ -1684,6 +1742,7 @@ export const removeTime: (self: DateTime) => Utc = Internal.removeTime * const selectedParts = [parts.year, parts.month, parts.day, parts.hour] // => [2024, 1, 1, 12] * ``` * + * @stability stable * @category getters * @since 3.6.0 */ @@ -1709,6 +1768,7 @@ export const toParts: (self: DateTime) => DateTime.PartsWithWeekday = Internal.t * const selectedParts = [parts.year, parts.month, parts.day, parts.hour] // => [2024, 1, 1, 12] * ``` * + * @stability stable * @category getters * @since 3.6.0 */ @@ -1730,6 +1790,7 @@ export const toPartsUtc: (self: DateTime) => DateTime.PartsWithWeekday = Interna * DateTime.getPartUtc(dateTime, "year") // => 2024 * ``` * + * @stability stable * @category getters * @since 3.6.0 */ @@ -1756,6 +1817,7 @@ export const getPartUtc: { * DateTime.getPart(dateTime, "year") // => 2024 * ``` * + * @stability stable * @category getters * @since 3.6.0 */ @@ -1786,6 +1848,7 @@ export const getPart: { * updated // => DateTime.makeZonedUnsafe("2025-06-15T12:00:00Z", { timeZone: "UTC" }) * ``` * + * @stability stable * @category transforming * @since 3.6.0 */ @@ -1815,6 +1878,7 @@ export const setParts: { * updated // => DateTime.makeUnsafe("2025-01-01T18:00:00Z") * ``` * + * @stability stable * @category transforming * @since 3.6.0 */ @@ -1851,6 +1915,7 @@ export const setPartsUtc: { * await Effect.runPromise(Effect.provide(program, layer)) // => "Europe/London" * ``` * + * @stability stable * @category services * @since 3.11.0 */ @@ -1873,6 +1938,7 @@ export class CurrentTimeZone extends Context.Service( * }).pipe(DateTime.withCurrentZoneNamed("Europe/London"))) // => "Europe/London" * ``` * + * @stability stable * @category accessors * @since 3.6.0 */ @@ -1895,6 +1961,7 @@ export const setZoneCurrent = (self: DateTime): Effect.Effect "Europe/London" * ``` * + * @stability stable * @category providing services * @since 3.6.0 */ @@ -1917,6 +1984,7 @@ export const withCurrentZone: { * }).pipe(DateTime.withCurrentZoneLocal)) // => true * ``` * + * @stability stable * @category providing services * @since 3.6.0 */ @@ -1940,6 +2008,7 @@ export const withCurrentZoneLocal = ( * await Effect.runPromise(program) // => "+03:00" * ``` * + * @stability stable * @category providing services * @since 3.6.0 */ @@ -1973,6 +2042,7 @@ export const withCurrentZoneOffset: { * }).pipe(DateTime.withCurrentZoneNamed("Europe/London"))) // => "Europe/London" * ``` * + * @stability stable * @category providing services * @since 3.6.0 */ @@ -2006,6 +2076,7 @@ export const withCurrentZoneNamed: { * }).pipe(DateTime.withCurrentZoneNamed("Europe/London"))) // => "Europe/London" * ``` * + * @stability stable * @category accessors * @since 3.6.0 */ @@ -2043,6 +2114,7 @@ export const nowInCurrentZone: Effect.Effect = Ef * }) * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2086,6 +2158,7 @@ export const mutate: { * modified // => DateTime.makeZonedUnsafe("2024-01-01T18:00:00Z", { timeZone: "Europe/London" }) * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2110,6 +2183,7 @@ export const mutateUtc: { * result // => DateTime.makeUnsafe(10) * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2139,6 +2213,7 @@ export const mapEpochMillis: { * ) // => 3600000 * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2167,6 +2242,7 @@ export const withDate: { * ) // => 0 * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2202,6 +2278,7 @@ export const withDateUtc: { * result2 // => "Zoned: 2024-06-15T15:30:00.000+01:00[Europe/London]" * ``` * + * @stability stable * @category mapping * @since 3.6.0 */ @@ -2253,6 +2330,7 @@ export const match: { * @see {@link add} for calendar-aware date/time part arithmetic * @see {@link subtractDuration} for subtracting an elapsed duration * + * @stability stable * @category math * @since 3.6.0 */ @@ -2275,6 +2353,7 @@ export const addDuration: { * ) // => DateTime.makeUnsafe(-300000) * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2302,6 +2381,7 @@ export const subtractDuration: { * ) // => DateTime.makeUnsafe(300000) * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2324,6 +2404,7 @@ export const add: { * ) // => DateTime.makeUnsafe(-300000) * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2351,6 +2432,7 @@ export const subtract: { * ) // => DateTime.makeUnsafe("2024-01-01T00:00:00Z") * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2385,6 +2467,7 @@ export const startOf: { * ) // => DateTime.makeUnsafe("2024-01-01T23:59:59.999Z") * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2419,6 +2502,7 @@ export const endOf: { * ) // => DateTime.makeUnsafe("2024-01-02T00:00:00Z") * ``` * + * @stability stable * @category math * @since 3.6.0 */ @@ -2466,6 +2550,7 @@ export const nearest: { * }) // => "Saturday, June 15, 2024 at 3:30 PM" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2508,6 +2593,7 @@ export const format: { * }) * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2556,6 +2642,7 @@ export const formatLocal: { * }) * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2613,6 +2700,7 @@ export const formatUtc: { * @see {@link formatUtc} for formatting with options forced to UTC * @see {@link formatIso} for stable ISO formatting * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2641,6 +2729,7 @@ export const formatIntl: { * DateTime.formatIso(zoned) // => "2024-01-01T12:30:45.123Z" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2667,6 +2756,7 @@ export const formatIso: (self: DateTime) => string = Internal.formatIso * DateTime.formatIsoDate(zoned) // => "2024-01-02" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2693,6 +2783,7 @@ export const formatIsoDate: (self: DateTime) => string = Internal.formatIsoDate * DateTime.formatIsoDateUtc(zoned) // => "2024-01-01" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2720,6 +2811,7 @@ export const formatIsoDateUtc: (self: DateTime) => string = Internal.formatIsoDa * DateTime.formatIsoOffset(zoned) // => "2024-01-01T15:00:00.000+03:00" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2750,6 +2842,7 @@ export const formatIsoOffset: (self: DateTime) => string = Internal.formatIsoOff * DateTime.formatIsoZoned(offsetZone) // => "2024-06-15T17:30:45.123+03:00" * ``` * + * @stability stable * @category formatting * @since 3.6.0 */ @@ -2779,6 +2872,7 @@ export const formatIsoZoned: (self: Zoned) => string = Internal.formatIsoZoned * await Effect.runPromise(Effect.provide(program, layer)) // => "Europe/London" * ``` * + * @stability stable * @category layers * @since 3.6.0 */ @@ -2809,6 +2903,7 @@ export const layerCurrentZone: (resource: NoInfer) => Layer.Layer "+03:00" * ``` * + * @stability stable * @category layers * @since 3.6.0 */ @@ -2838,6 +2933,7 @@ export const layerCurrentZoneOffset = (offset: number): Layer.Layer "Europe/London" * ``` * + * @stability stable * @category layers * @since 3.6.0 */ @@ -2868,6 +2964,7 @@ export const layerCurrentZoneNamed: (zoneId: string) => Layer.Layer< * await Effect.runPromise(Effect.provide(program, DateTime.layerCurrentZoneLocal)) // => true * ``` * + * @stability stable * @category layers * @since 3.6.0 */ diff --git a/repos/effect/packages/effect/src/Deferred.ts b/packages/effect/src/Deferred.ts similarity index 98% rename from repos/effect/packages/effect/src/Deferred.ts rename to packages/effect/src/Deferred.ts index 14e75b964..663d79a2d 100644 --- a/repos/effect/packages/effect/src/Deferred.ts +++ b/packages/effect/src/Deferred.ts @@ -5,6 +5,7 @@ * `Deferred` suspends the fiber instead of blocking an operating-system thread, * and every waiter observes the same completion. * + * @stability stable * @since 2.0.0 */ import type * as Cause from "./Cause.ts" @@ -52,6 +53,7 @@ const TypeId = "~effect/Deferred" * await Effect.runPromise(program) // => "Hello, World!" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -68,6 +70,7 @@ export interface Deferred extends Deferred.Variance< * Use to validate unknown values at runtime boundaries before treating them as * `Deferred` values. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -80,6 +83,7 @@ export const isDeferred = (u: unknown): u is Deferred => hasProperty * * Use to reference type-level metadata associated with `Deferred`. * + * @stability stable * @since 2.0.0 */ export declare namespace Deferred { @@ -140,6 +144,7 @@ DeferredImpl.prototype = DeferredProto * Deferred.isDoneUnsafe(deferred) // => false * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -166,6 +171,7 @@ export const makeUnsafe = (): Deferred => new DeferredImpl 42 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -218,6 +224,7 @@ export { * @see {@link complete} for completing from an effect and memoizing its result * @see {@link completeWith} for completing with an effect directly * + * @stability stable * @category getters * @since 2.0.0 */ @@ -255,6 +262,7 @@ export { * * @see {@link completeWith} for storing an effect directly without memoizing its result * + * @stability stable * @category completion * @since 2.0.0 */ @@ -303,6 +311,7 @@ export const complete: { * @see {@link complete} for running an effect once and sharing its result * @see {@link done} for completing from an already computed `Exit` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -347,6 +356,7 @@ export const completeWith: { * @see {@link succeed} for completing with a success value * @see {@link failCause} for completing with a failure cause * + * @stability stable * @category completion * @since 2.0.0 */ @@ -383,6 +393,7 @@ export const done: { * await Effect.runPromise(program) // => [true, Exit.fail("Operation failed")] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -421,6 +432,7 @@ export const fail: { * await Effect.runPromise(program) // => [true, Exit.fail("Lazy error")] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -461,6 +473,7 @@ export const failSync: { * await Effect.runPromise(program) // => [true, Exit.failCause(Cause.fail("Operation failed"))] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -502,6 +515,7 @@ export const failCause: { * await Effect.runPromise(program) // => [true, Exit.failCause(Cause.fail("Lazy error"))] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -543,6 +557,7 @@ export const failCauseSync: { * await Effect.runPromise(program) // => [true, Exit.die(defect)] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -581,6 +596,7 @@ export const die: { * await Effect.runPromise(program) // => [true, Exit.die(defect)] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -624,6 +640,7 @@ export const dieSync: { * Exit.hasInterrupts(exit) // => true * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -659,6 +676,7 @@ export const interrupt = (self: Deferred): Effect => * await Effect.runPromise(program) // => [true, Exit.interrupt(42)] * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -695,6 +713,7 @@ export const interruptWith: { * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -711,6 +730,7 @@ export const isDone = (self: Deferred): Effect => internalE * @see {@link isDone} for checking completion inside `Effect` * @see {@link poll} for reading the completed effect when available * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -743,6 +763,7 @@ export const isDoneUnsafe = (self: Deferred): boolean => self.effect * await Effect.runPromise(program) // => [Option.none(), Option.some(42)] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -778,6 +799,7 @@ export function poll(self: Deferred): Effect 42 * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -815,6 +837,7 @@ export const succeed: { * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -851,6 +874,7 @@ export const sync: { * Deferred.doneUnsafe(deferred, Effect.succeed(42)) // => true * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -903,6 +927,7 @@ export const doneUnsafe = (self: Deferred, effect: Effect): bo * await Effect.runPromise(program) // => [true, 42] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Differ.ts b/packages/effect/src/Differ.ts similarity index 95% rename from repos/effect/packages/effect/src/Differ.ts rename to packages/effect/src/Differ.ts index ba636ba64..3480e789a 100644 --- a/repos/effect/packages/effect/src/Differ.ts +++ b/packages/effect/src/Differ.ts @@ -4,6 +4,7 @@ * difference, combines patches in order, and applies a patch to an old value to * produce an updated value. * + * @stability stable * @since 4.0.0 */ @@ -21,6 +22,7 @@ * combines patches, and applies a patch to an old value to produce an updated * value. * + * @stability stable * @category models * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Duration.ts b/packages/effect/src/Duration.ts similarity index 97% rename from repos/effect/packages/effect/src/Duration.ts rename to packages/effect/src/Duration.ts index 2ffd92993..e93556fa9 100644 --- a/repos/effect/packages/effect/src/Duration.ts +++ b/packages/effect/src/Duration.ts @@ -7,6 +7,7 @@ * from common input shapes, unit conversions, comparisons, arithmetic, * formatting, and reusable reducer or combiner helpers. * + * @stability stable * @since 2.0.0 */ import * as Combiner from "./Combiner.ts" @@ -78,6 +79,7 @@ const nanosToHrTime = (nanos: bigint): [seconds: number, nanos: number] => { * @see {@link DurationValue} for the tagged representation exposed by the * `value` field * + * @stability stable * @category models * @since 2.0.0 */ @@ -104,6 +106,7 @@ export interface Duration extends Equal.Equal, Pipeable, Inspectable.Inspectable * representation * @see {@link match} for pattern matching without reading `value` directly * + * @stability stable * @category models * @since 2.0.0 */ @@ -123,6 +126,7 @@ export type DurationValue = * * @see {@link Input} for the full duration input union * + * @stability stable * @category models * @since 2.0.0 */ @@ -166,6 +170,7 @@ export type Unit = * @see {@link DurationObject} for object-shaped duration input * @see {@link Unit} for supported string units * + * @stability stable * @category models * @since 4.0.0 */ @@ -197,6 +202,7 @@ export type Input = * Duration.fromInputUnsafe({ seconds: 1, nanoseconds: 500 }) // => Duration.nanos(1_000_000_500n) * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -236,6 +242,7 @@ const DURATION_REGEXP = /^(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|m * Duration.fromInputUnsafe([2, 500_000_000]) // => Duration.nanos(2_500_000_000n) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -337,6 +344,7 @@ const invalid = (input: unknown): never => { * Duration.fromInput("invalid" as any) // => Option.none() * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -431,6 +439,7 @@ const make = (input: number | bigint): Duration => { * Duration.isDuration(1000) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -448,6 +457,7 @@ export const isDuration = (u: unknown): u is Duration => hasProperty(u, TypeId) * Duration.isFinite(Duration.infinity) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -466,6 +476,7 @@ export const isFinite = (self: Duration): boolean => * Duration.isZero(Duration.seconds(1)) // => false * ``` * + * @stability stable * @category predicates * @since 3.5.0 */ @@ -494,6 +505,7 @@ export const isZero = (self: Duration): boolean => { * Duration.isNegative(Duration.negativeInfinity) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -523,6 +535,7 @@ export const isNegative = (self: Duration): boolean => { * Duration.isPositive(Duration.infinity) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -551,6 +564,7 @@ export const isPositive = (self: Duration): boolean => { * Duration.abs(Duration.negativeInfinity) // => Duration.infinity * ``` * + * @stability stable * @category math * @since 4.0.0 */ @@ -578,6 +592,7 @@ export const abs = (self: Duration): Duration => { * Duration.negate(Duration.infinity) // => Duration.negativeInfinity * ``` * + * @stability stable * @category math * @since 4.0.0 */ @@ -605,6 +620,7 @@ export const negate = (self: Duration): Duration => { * Duration.toMillis(Duration.zero) // => 0 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -621,6 +637,7 @@ export const zero: Duration = make(0) * Duration.toMillis(Duration.infinity) // => Infinity * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -637,6 +654,7 @@ export const infinity: Duration = make(Infinity) * Duration.toMillis(Duration.negativeInfinity) // => -Infinity * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -653,6 +671,7 @@ export const negativeInfinity: Duration = make(-Infinity) * Duration.nanos(500_000_000n) // => Duration.nanos(500_000_000n) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -669,6 +688,7 @@ export const nanos = (nanos: bigint): Duration => make(nanos) * Duration.micros(500_000n) // => Duration.nanos(500_000_000n) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -685,6 +705,7 @@ export const micros = (micros: bigint): Duration => make(micros * bigint1e3) * Duration.toMillis(Duration.millis(1000)) // => 1000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -701,6 +722,7 @@ export const millis = (millis: number): Duration => make(millis) * Duration.toMillis(Duration.seconds(30)) // => 30_000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -717,6 +739,7 @@ export const seconds = (seconds: number): Duration => make(seconds * 1000) * Duration.toMillis(Duration.minutes(5)) // => 300_000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -733,6 +756,7 @@ export const minutes = (minutes: number): Duration => make(minutes * 60_000) * Duration.toMillis(Duration.hours(2)) // => 7_200_000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -749,6 +773,7 @@ export const hours = (hours: number): Duration => make(hours * 3_600_000) * Duration.toMillis(Duration.days(1)) // => 86_400_000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -765,6 +790,7 @@ export const days = (days: number): Duration => make(days * 86_400_000) * Duration.toMillis(Duration.weeks(1)) // => 604_800_000 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -782,6 +808,7 @@ export const weeks = (weeks: number): Duration => make(weeks * 604_800_000) * Duration.toMillis(Duration.minutes(2)) // => 120_000 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -805,6 +832,7 @@ export const toMillis = (self: Input): number => * Duration.toSeconds(Duration.minutes(2)) // => 120 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -828,6 +856,7 @@ export const toSeconds = (self: Input): number => * Duration.toMinutes(Duration.hours(1)) // => 60 * ``` * + * @stability stable * @category getters * @since 3.8.0 */ @@ -851,6 +880,7 @@ export const toMinutes = (self: Input): number => * Duration.toHours(Duration.days(1)) // => 24 * ``` * + * @stability stable * @category getters * @since 3.8.0 */ @@ -874,6 +904,7 @@ export const toHours = (self: Input): number => * Duration.toDays(Duration.weeks(1)) // => 7 * ``` * + * @stability stable * @category getters * @since 3.8.0 */ @@ -897,6 +928,7 @@ export const toDays = (self: Input): number => * Duration.toWeeks(Duration.days(7)) // => 1 * ``` * + * @stability stable * @category getters * @since 3.8.0 */ @@ -936,6 +968,7 @@ export const toWeeks = (self: Input): number => * // throws Error: "Cannot convert infinite duration to nanos" * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -968,6 +1001,7 @@ export const toNanosUnsafe = (input: Input): bigint => { * Duration.toNanos(Duration.infinity) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -984,6 +1018,7 @@ export const toNanos: (self: Input) => Option.Option = Option.liftThrowa * Duration.toHrTime(Duration.millis(1500)) // => [1, 500_000_000] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1022,6 +1057,7 @@ export const toHrTime = (input: Input): [seconds: number, nanos: number] => { * }) // => "5000 milliseconds" * ``` * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -1079,6 +1115,7 @@ export const match: { * }) // => 5000 * ``` * + * @stability stable * @category pattern matching * @since 4.0.0 */ @@ -1142,6 +1179,7 @@ export const matchPair: { * durations.sort((a, b) => Duration.Order(a, b)).map(Duration.toSeconds) // => [1, 2, 3] * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -1193,6 +1231,7 @@ export const Order: order.Order = order.make((self, that) => * @see {@link isGreaterThanOrEqualTo} for checking only the lower bound * @see {@link isLessThanOrEqualTo} for checking only the upper bound * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1212,6 +1251,7 @@ export const between: { * Duration.Equivalence(Duration.seconds(5), Duration.millis(5000)) // => true * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -1233,6 +1273,7 @@ export const Equivalence: Equ.Equivalence = (self, that) => * Duration.min(Duration.seconds(5), Duration.seconds(3)) // => Duration.seconds(3) * ``` * + * @stability stable * @category ordering * @since 2.0.0 */ @@ -1252,6 +1293,7 @@ export const min: { * Duration.max(Duration.seconds(5), Duration.seconds(3)) // => Duration.seconds(5) * ``` * + * @stability stable * @category ordering * @since 2.0.0 */ @@ -1274,6 +1316,7 @@ export const max: { * }) // => Duration.seconds(5) * ``` * + * @stability stable * @category ordering * @since 2.0.0 */ @@ -1300,6 +1343,7 @@ export const clamp: { * Duration.divide(Duration.seconds(10), 0) // => Option.none() * ``` * + * @stability stable * @category math * @since 2.4.19 */ @@ -1352,6 +1396,7 @@ export const divide: { * Duration.divideUnsafe(Duration.seconds(10), 0) // => Duration.infinity * ``` * + * @stability stable * @category math * @since 4.0.0 */ @@ -1402,6 +1447,7 @@ export const divideUnsafe: { * Duration.times(Duration.seconds(5), 2) // => Duration.seconds(10) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -1439,6 +1485,7 @@ export const times: { * Duration.subtract(Duration.seconds(10), Duration.seconds(3)) // => Duration.seconds(7) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -1482,6 +1529,7 @@ export const subtract: { * Duration.sum(Duration.seconds(5), Duration.seconds(3)) // => Duration.seconds(8) * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -1518,6 +1566,7 @@ export const sum: { * Duration.isLessThan(Duration.seconds(3), Duration.seconds(5)) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1540,6 +1589,7 @@ export const isLessThan: { * ) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1559,6 +1609,7 @@ export const isLessThanOrEqualTo: { * Duration.isGreaterThan(Duration.seconds(5), Duration.seconds(3)) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1581,6 +1632,7 @@ export const isGreaterThan: { * ) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1600,6 +1652,7 @@ export const isGreaterThanOrEqualTo: { * Duration.equals(Duration.seconds(5), Duration.millis(5000)) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1636,6 +1689,7 @@ export const equals: { * Duration.parts(complex) // => ({ days: 1, hours: 2, minutes: 30, seconds: 0, millis: 0, nanos: 0 }) * ``` * + * @stability stable * @category converting * @since 3.8.0 */ @@ -1700,6 +1754,7 @@ export const parts = (self: Duration): { * Duration.format(Duration.millis(1001)) // => "1s 1ms" * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1762,6 +1817,7 @@ export const format = (self: Duration): string => { * @see {@link CombinerMax} for keeping the longest duration instead of summing * @see {@link CombinerMin} for keeping the shortest duration instead of summing * + * @stability stable * @category math * @since 4.0.0 */ @@ -1777,6 +1833,7 @@ export const ReducerSum: Reducer.Reducer = Reducer.make(sum, zero) * @see {@link CombinerMin} for keeping the shortest `Duration` * @see {@link max} for comparing two `Duration` values directly * + * @stability stable * @category math * @since 4.0.0 */ @@ -1792,6 +1849,7 @@ export const CombinerMax: Combiner.Combiner = Combiner.max(Order) * @see {@link CombinerMax} for keeping the longest `Duration` * @see {@link min} for comparing two `Duration` values directly * + * @stability stable * @category math * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts similarity index 98% rename from repos/effect/packages/effect/src/Effect.ts rename to packages/effect/src/Effect.ts index a4da72c9b..ac90f3f94 100644 --- a/repos/effect/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -8,6 +8,7 @@ * creating effects, combining them, handling failures, managing resources, and * running effect programs. * + * @stability stable * @since 2.0.0 */ import type * as Arr from "./Array.ts" @@ -76,6 +77,7 @@ import type * as Unify from "./Unify.ts" /** * Type-level identifier for `Effect` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -84,6 +86,7 @@ export type TypeId = "~effect/Effect" /** * Runtime identifier used to recognize `Effect` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -110,6 +113,7 @@ export const TypeId: TypeId = core.EffectTypeId * To run an `Effect` value, you need a `Runtime`, which is a type that is * capable of executing `Effect` values. * + * @stability stable * @category models * @since 2.0.0 */ @@ -124,6 +128,7 @@ export interface Effect extends Pipeable, I /** * Type-level unification support for `Effect` values. * + * @stability stable * @category models * @since 2.0.0 */ @@ -137,6 +142,7 @@ export interface EffectUnify { /** * Type lambda used to represent `Effect` in higher-kinded APIs. * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -147,6 +153,7 @@ export interface EffectTypeLambda extends TypeLambda { /** * Variance interface for Effect, encoding the type parameters' variance. * + * @stability stable * @category models * @since 2.0.0 */ @@ -167,6 +174,7 @@ export interface Variance { * @see {@link Error} for extracting the failure type from the same `Effect` * @see {@link Services} for extracting the required services from the same `Effect` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -188,6 +196,7 @@ export type Success = T extends Effect ? _A * @see {@link Success} for extracting the success value type instead * @see {@link Services} for extracting the required services type instead * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -205,6 +214,7 @@ export type Error = T extends Effect ? _E * @see {@link Success} for extracting the success value type instead * @see {@link Error} for extracting the failure type instead * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -223,6 +233,7 @@ export type Services = T extends Effect ? _R * Effect.isEffect("hello") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -238,6 +249,7 @@ export const isEffect: (u: unknown) => u is Effect = * * @see {@link gen} for writing generator-based `Effect` programs that consume this iterator protocol * + * @stability stable * @category models * @since 4.0.0 */ @@ -255,6 +267,7 @@ export interface EffectIterator> { * Namespace containing type utilities for the `Effect.all` function, which handles * collecting multiple effects into various output structures. * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -485,6 +498,7 @@ export declare namespace All { * ``` * * @see {@link forEach} for iterating over elements and applying an effect. + * @stability stable * @category collecting * @since 2.0.0 */ @@ -526,6 +540,7 @@ export const all: < * await Effect.runPromise(program) // => [[1, 3], ['0 is even', '2 is even']] * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -575,6 +590,7 @@ export const partition: { * output // => ["Adding 1 at index 0", "Adding 2 at index 1", "Adding 3 at index 2", 6] * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -614,6 +630,7 @@ export const reduce: { * await Effect.runPromiseExit(program) // => Exit.fail(["0 is even", "2 is even"]) * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -668,6 +685,7 @@ export const validate: { * await Effect.runPromise(program) // => Option.some(3) * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -696,6 +714,7 @@ export const findFirst: { * * @see {@link findFirst} for the simpler effectful predicate-based variant * + * @stability stable * @category collecting * @since 4.0.0 */ @@ -770,6 +789,7 @@ export const findFirstFilter: { * ``` * * @see {@link all} for combining multiple effects into one. + * @stability stable * @category collecting * @since 2.0.0 */ @@ -808,6 +828,7 @@ export const forEach: { * await Effect.runPromise(empty) // => Option.none() * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -836,6 +857,7 @@ export const head: ( * output // => ["Current count: 1", "Current count: 2", "Current count: 3", "Current count: 4", "Current count: 5"] * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -886,6 +908,7 @@ export const whileLoop: (options: { * ``` * * @see {@link tryPromise} for a version that can handle failures. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -960,6 +983,7 @@ export const promise: ( * ``` * * @see {@link promise} if the effectful computation is asynchronous and does not throw errors. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -992,6 +1016,7 @@ export const tryPromise: { * ``` * * @see {@link fail} to create an effect that represents a failure. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1010,6 +1035,7 @@ export const succeed: (value: A) => Effect = internal.succeed * Effect.runSync(program) // => Option.none() * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1028,6 +1054,7 @@ export const succeedNone: Effect> = internal.succeedNone * Effect.runSync(program) // => Option.some(42) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1119,6 +1146,7 @@ export const succeedSome: (value: A) => Effect> = internal.succeedS * Effect.runSync(withSuspend(6, 2)) // => 3 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1162,6 +1190,7 @@ export const suspend: ( * ``` * * @see {@link try_ | try} for a version that can handle failures. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1172,6 +1201,7 @@ export { /** * Returns an effect that succeeds with `void`. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1183,6 +1213,7 @@ export { /** * Returns an effect that succeeds with `undefined`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1222,6 +1253,7 @@ export { * output // => ["callback completed"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1246,6 +1278,7 @@ export const callback: ( * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1270,6 +1303,7 @@ export const never: Effect = internal.never * Effect.runSync(program) // => { x: 2, y: 3, sum: 5 } * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -1287,6 +1321,7 @@ export const Do: Effect<{}> = internal.Do * @see {@link Do} for starting from an empty accumulated record * @see {@link bind} for adding fields produced by effects * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -1329,6 +1364,7 @@ export { * @see {@link Do} for starting from an empty accumulated record * @see {@link gen} for sequencing without accumulating a record * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -1359,6 +1395,7 @@ export { * @see {@link bindTo} for naming the success value of an existing effect * @see {@link gen} for generator-based sequencing without accumulating a record * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -1425,6 +1462,7 @@ export const bind: { * await Effect.runPromise(program) // => "Final amount to charge: 96" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1459,6 +1497,7 @@ export const gen: { /** * Type helpers for `Effect.gen` generator return signatures. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1500,6 +1539,7 @@ export declare namespace gen { * ``` * * @see {@link succeed} to create an effect that represents a successful value. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1528,6 +1568,7 @@ export const fail: (error: E) => Effect = internal.fail * Effect.runSync(Effect.flip(program)).operation // => "sync" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1559,6 +1600,7 @@ export const failSync: (evaluate: LazyArg) => Effect = internal. * Effect.runSync(Effect.flip(program)) // => "Network error" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1587,6 +1629,7 @@ export const failCause: (cause: Cause.Cause) => Effect = interna * Effect.runSync(Effect.flip(program)) // => "Error computed at runtime" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1629,6 +1672,7 @@ export const failCauseSync: ( * Effect.runSyncExit(program) // => Exit.die(defect) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1703,6 +1747,7 @@ export { * * @see {@link sync} if the effectful computation is synchronous and does not * throw errors. + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1728,6 +1773,7 @@ export { * output // => ["Before yield", "After yield"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1752,6 +1798,7 @@ export const yieldNow: Effect = internal.yieldNow * output // => ["High priority task", "Continued after yield"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1770,6 +1817,7 @@ export const yieldNowWith: (priority?: number) => Effect = internal.yieldN * Effect.runSync(program) // => "number" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1790,6 +1838,7 @@ export const withFiber: ( * Effect.runSync(program) // => "number" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1821,6 +1870,7 @@ export const withFiberSucceed: ( * output // => [42, "Something went wrong"] * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1860,6 +1910,7 @@ export const fromResult: (result: Result.Result) => Effect = i * output // => [42, "NoSuchElementError", "missing"] * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1907,6 +1958,7 @@ export const fromOption: < * Effect.runSync(program) // => Option.some(42) * ``` * + * @stability stable * @category converting * @since 3.13.0 */ @@ -1936,6 +1988,7 @@ export const transposeOption: ( * output // => ["missing", "hello"] * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -2015,6 +2068,7 @@ export const fromNullishOr: (value: A) => Effect, Cause.NoSuch * ``` * * @see {@link tap} for a version that ignores the result of the effect. + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2048,6 +2102,7 @@ export const flatMap: { * output // => ["hello"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2132,6 +2187,7 @@ export const flatten: (self: Effect, E2, R2>) = * await Effect.runPromise(result2) // => 190 * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2200,6 +2256,7 @@ export const andThen: { * output // => ["Apply a discount to: 100", 95] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2268,6 +2325,7 @@ export const tap: { * @see {@link option} for a version that uses `Option` instead. * @see {@link exit} for a version that encapsulates both recoverable errors and defects in an `Exit`. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2311,6 +2369,7 @@ export const result: (self: Effect) => Effect(self: Effect) => Effect, never * @see {@link option} for a version that uses `Option` instead. * @see {@link result} for a version that uses `Result` instead. * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -2419,6 +2479,7 @@ export const exit: ( * @see {@link mapError} for a version that operates on the error channel. * @see {@link mapBoth} for a version that operates on both channels. * @see {@link flatMap} or {@link andThen} for a version that can return a new effect. + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2454,6 +2515,7 @@ export const map: { * @see {@link map} for deriving the replacement value from the success value * @see {@link asVoid} for replacing the success value with `void` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2475,6 +2537,7 @@ export const as: { * Effect.runSync(program) // => Option.some(42) * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2493,6 +2556,7 @@ export const asSome: (self: Effect) => Effect, E, R> * Effect.runSync(program) // => undefined * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2524,6 +2588,7 @@ export const asVoid: (self: Effect) => Effect = in * Effect.runSync(flipped) // => "Oh uh!" * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2584,6 +2649,7 @@ export const flip: (self: Effect) => Effect = interna * @see {@link zipWith} for a version that combines the results with a custom function. * @see {@link all} for collecting a larger structure of effects. * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2633,6 +2699,7 @@ export const zip: { * Effect.runSync(task3) // => 6 * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -2687,6 +2754,7 @@ export { * * @see {@link catchCause} for a version that can recover from both recoverable and unrecoverable errors. * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2737,6 +2805,7 @@ export { * @see {@link catchTags} for handling multiple tagged errors in one call * @see {@link catchIf} for recovering from errors that match a predicate * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -2836,6 +2905,7 @@ export const catchTag: { * Effect.runSync(handled) // => "Network error: 503" * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -2949,6 +3019,7 @@ export const catchTags: { * * @see {@link catchReasons} for handling several nested reason tags * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3046,6 +3117,7 @@ export const catchReason: { * Effect.runSync(handled) // => "Quota exceeded: 100" * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3152,6 +3224,7 @@ export const catchReasons: { * @see {@link catchReason} for handling one nested reason tag * @see {@link catchReasons} for handling several nested reason tags * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3190,6 +3263,7 @@ export type TagsWithReason = { * Effect.runSync(Effect.flip(unwrapped))._tag // => "RateLimitError" * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3252,6 +3326,7 @@ export const unwrapReason: { * output // => ["Caught defect", "Recovered from defect"] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3305,6 +3380,7 @@ export const catchCause: { * output // => ["Caught defect: Unexpected error", "Recovered from defect"] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3359,6 +3435,7 @@ export const catchDefect: { * Effect.runSync(Effect.all([recovered, recovered2])) // => ['missing:user-1', 'missing:user-1'] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3412,6 +3489,7 @@ export const catchIf: { * @see {@link catchTags} for recovering from several tagged errors * @see {@link catchCauseFilter} for filtering full causes instead of typed errors * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3462,6 +3540,7 @@ export const catchFilter: { * @see {@link fromNullishOr} for converting nullish values into `NoSuchElementError` * @see {@link option} for converting any failure into `Option.none` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3509,6 +3588,7 @@ export const catchNoSuchElement: ( * @see {@link catchCauseFilter} for selecting full causes with a `Filter` * @see {@link catchIf} for predicate-based recovery from typed errors * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3543,6 +3623,7 @@ export const catchCauseIf: { * @see {@link catchFilter} for filtering typed error values instead of full causes * @see {@link catchCause} for recovering from every cause without filtering * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3594,6 +3675,7 @@ export const catchCauseFilter: { * @see {@link map} for a version that operates on the success channel. * @see {@link mapBoth} for a version that operates on both channels. * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3640,6 +3722,7 @@ export const mapError: { * @see {@link map} for a version that operates on the success channel. * @see {@link mapError} for a version that operates on the error channel. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -3681,6 +3764,7 @@ export const mapBoth: { * Effect.runSyncExit(program) // => Exit.die(new DivideByZeroError()) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3715,6 +3799,7 @@ export const orDie: (self: Effect) => Effect = in * output // => ["expected error: NetworkError", Exit.fail("NetworkError")] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3762,6 +3847,7 @@ export const tapError: { * output // => ["expected error: 504", Exit.fail(new NetworkError({ statusCode: 504 }))] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3817,6 +3903,7 @@ export const tapErrorTag: { * output // => ["Logging cause: Something went wrong", Exit.fail("Something went wrong")] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3858,6 +3945,7 @@ export const tapCause: { * output // => ["Logging failure cause: Network timeout", Exit.fail("Network timeout")] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3892,6 +3980,7 @@ export const tapCauseIf: { * @see {@link tapCause} for observing every failure cause * @see {@link catchCauseFilter} for recovering from selected causes instead of only observing them * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -3938,6 +4027,7 @@ export const tapCauseFilter: { * output // => ["defect: Something went wrong", Exit.die("Something went wrong")] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -3976,6 +4066,7 @@ export const tapDefect: { * output // => ["Attempt 1", "Attempt 2", "Attempt 3", "Ready"] * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -3988,6 +4079,7 @@ export const eventually: (self: Effect) => Effect /** * Type helpers for retrying effects. * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4083,6 +4175,7 @@ export declare namespace Retry { * * @see {@link retryOrElse} for a version that allows you to run a fallback. * @see {@link repeat} if your retry condition is based on successful outcomes rather than errors. + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4159,6 +4252,7 @@ export const retry: { * ``` * * @see {@link retry} for a version that does not run a fallback effect. + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4199,6 +4293,7 @@ export const retryOrElse: { * Effect.runSync(program) // => "Caught cause: Something went wrong" * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4245,6 +4340,7 @@ export const sandbox: ( * Effect.runSync(program) // => undefined * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4289,6 +4385,7 @@ export const ignore: < * Effect.runSync(program) // => undefined * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4381,6 +4478,7 @@ export const ignoreCause: < * events // => ["AttemptStart:0", "AttemptFailure:0", "AttemptStart:1", "AttemptSuccess:1"] * ``` * + * @stability unstable * @category error handling * @since 3.16.0 */ @@ -4410,6 +4508,7 @@ export const withExecutionPlan: { * If the `defectsOnly` option is set to `true`, only defects (unrecoverable * errors) will be reported, while regular failures will be ignored. * + * @stability unstable * @category error handling * @since 4.0.0 */ @@ -4459,6 +4558,7 @@ export const withErrorReporting: < * Effect.runSyncExit(program) // => Exit.succeed(0) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4512,6 +4612,7 @@ export const orElseSucceed: { * Effect.runSync(program) // => "secondary result" * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4557,6 +4658,7 @@ export const firstSuccessOf: >( * @see {@link timeoutOption} for returning `Option.none` on timeout. * @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers. * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -4597,6 +4699,7 @@ export const timeout: { * @see {@link timeout} for a version that raises a `TimeoutError`. * @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers. * + * @stability stable * @category delays & timeouts * @since 3.1.0 */ @@ -4634,6 +4737,7 @@ export const timeoutOption: { * @see {@link timeout} for failing with a `TimeoutError`. * @see {@link timeoutOption} for returning `Option.none` on timeout. * + * @stability stable * @category delays & timeouts * @since 4.0.0 */ @@ -4667,6 +4771,7 @@ export const timeoutOrElse: { * output // => ["Delayed message"] * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -4700,6 +4805,7 @@ export const delay: { * output // => ["Start", "End"] * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -4726,6 +4832,7 @@ export const sleep: (duration: Duration.Input) => Effect = internal.sleep * Effect.runSync(program) // => "ok" * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -4764,6 +4871,7 @@ export const timed: (self: Effect) => Effect<[duration: Durati * ``` * * @see {@link race} for a version that handles only two effects. + * @stability stable * @category racing * @since 2.0.0 */ @@ -4800,6 +4908,7 @@ export const raceAll: >( * await Effect.runPromise(Effect.flip(raced)) // => "First failed" * ``` * + * @stability stable * @category racing * @since 4.0.0 */ @@ -4840,6 +4949,7 @@ export const raceAllFirst: >( * output // => ["winner: slow-success"] * ``` * + * @stability stable * @category racing * @since 2.0.0 */ @@ -4897,6 +5007,7 @@ export const race: { * output // => ["failed: fast-fail"] * ``` * + * @stability stable * @category racing * @since 2.0.0 */ @@ -4945,6 +5056,7 @@ export const raceFirst: { * output // => [[2, 4], [2, 3]] * ``` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -4990,6 +5102,7 @@ export const filter: { * @see {@link filter} for keeping original elements with a boolean predicate, refinement, or effectful predicate * @see {@link filterMapEffect} for using an effectful `Filter` * + * @stability stable * @category collecting * @since 2.0.0 */ @@ -5024,6 +5137,7 @@ export const filterMap: { * @see {@link filterMap} for using a synchronous `Filter` * @see {@link filter} for keeping original elements with a predicate * + * @stability stable * @category collecting * @since 4.0.0 */ @@ -5072,6 +5186,7 @@ export const filterMapEffect: { * Effect.runSync(filtered) // => "Number 5 is odd" * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -5113,6 +5228,7 @@ export const filterOrElse: { * @see {@link filterOrElse} for using a predicate and fallback effect * @see {@link filterMapOrFail} for failing the effect when the filter fails * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -5155,6 +5271,7 @@ export const filterMapOrElse: { * Effect.runSync(Effect.flip(filtered)) // => "Expected even number, got 5" * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -5210,6 +5327,7 @@ export const filterOrFail: { * @see {@link filterOrFail} for validating with a predicate instead of a `Filter` * @see {@link filterMap} for filtering and mapping iterable elements * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -5269,6 +5387,7 @@ export const filterMapOrFail: { * output // => ["Condition is true!", Option.some(undefined)] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -5333,6 +5452,7 @@ export const when: { * ``` * * @see {@link matchEffect} if you need to perform side effects in the handlers. + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -5386,6 +5506,7 @@ export const match: { * * @see {@link match} for the non-eager version. * @see {@link matchEffect} if you need to perform side effects in the handlers. + * @stability stable * @category pattern matching * @since 4.0.0 */ @@ -5434,6 +5555,7 @@ export const matchEager: { * @see {@link matchCauseEffect} if you need to perform side effects in the * handlers. * @see {@link match} if you don't need to handle the cause of the failure. + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -5477,6 +5599,7 @@ export const matchCause: { * Effect.runSync(handleResult) // => "Success: 42" * ``` * + * @stability stable * @category pattern matching * @since 4.0.0 */ @@ -5511,6 +5634,7 @@ export const matchCauseEager: { * @see {@link matchCauseEager} for eager cause matching with pure handlers * @see {@link matchEffect} for effectful matching on typed failures instead of full causes * + * @stability stable * @category pattern matching * @since 4.0.0 */ @@ -5585,6 +5709,7 @@ export const matchCauseEffectEager: { * @see {@link matchCause} if you don't need side effects and only want to handle the result or failure. * @see {@link matchEffect} if you don't need to handle the cause of the failure. * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -5649,6 +5774,7 @@ export const matchCauseEffect: { * * @see {@link match} if you don't need side effects and only want to handle the * result or failure. + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -5692,6 +5818,7 @@ export const matchEffect: { * output // => [true] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -5722,6 +5849,7 @@ export const isFailure: (self: Effect) => Effect ["ok: true", "failed: false"] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -5779,6 +5907,7 @@ export const isSuccess: (self: Effect) => Effect() => Effect, never, R> = in * @see {@link context} for reading the complete context as a value * @see {@link service} for reading one service from the context * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -5875,6 +6005,7 @@ export const contextWith: ( * await Effect.runPromise(provided) // => "Result for: SELECT * FROM users" * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -5966,6 +6097,7 @@ export const provide: { * output // => ["Querying database", "result"] * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -6017,6 +6149,7 @@ export const provideContext: { * @see {@link provideContext} for partially satisfying an effect's context requirements. * @see {@link updateContext} for deriving the required context from the current one. * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -6050,6 +6183,7 @@ export const setContext: { * Effect.runSync(runnable) // => "Result for: SELECT * FROM users" * ``` * + * @stability stable * @category accessors * @since 4.0.0 */ @@ -6096,6 +6230,7 @@ export const service: (service: Context.Key) => Effect * output // => ["Service not available"] * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -6140,6 +6275,7 @@ export const serviceOption: (key: Context.Key) => Effect> * Effect.runSync(result) // => "Hello World!" * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -6185,6 +6321,7 @@ export const updateContext: { * output // => ["Updated count: 1", 1] * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -6256,6 +6393,7 @@ export const updateService: { * * @see {@link updateService} for updating a service only within a wrapped effect * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -6311,6 +6449,7 @@ export const updateServiceScoped: ( * @see {@link provide} for providing multiple layers to an effect. * @see {@link provideServiceEffect} for acquiring the service implementation effectfully. * @see {@link provideContext} for providing a complete context. + * @stability stable * @category providing services * @since 2.0.0 */ @@ -6384,6 +6523,7 @@ export const provideService: { * output // => ["Establishing database connection...", "Database connected!", "Result for: SELECT * FROM users"] * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -6429,6 +6569,7 @@ export const provideServiceEffect: { * output // => ["Got scope for resource management", "Acquiring resource", "Releasing resource", "resource"] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6469,6 +6610,7 @@ export const scope: Effect = internal.scope * output // => ["Acquiring resource", "Using resource", "Releasing resource"] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6513,6 +6655,7 @@ export const scoped: ( * output // => ["Inside scoped context", "Acquiring resource", "Releasing resource", "Manual finalizer", "resource"] * ``` * + * @stability stable * @category resource management * @since 3.11.0 */ @@ -6582,6 +6725,7 @@ export const scopedWith: ( * @see {@link acquireDisposable} for resources that implement JavaScript disposal protocols * @see {@link acquireUseRelease} for bracketing acquire, use, and release in one effect * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6636,6 +6780,7 @@ export const acquireRelease: ( * * @see {@link acquireRelease} for resources that need an explicit finalizer * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -6714,6 +6859,7 @@ export const acquireDisposable: ( * * @see {@link acquireRelease} for scoped resources whose use happens later * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6766,6 +6912,7 @@ export const acquireUseRelease: ( * @see {@link acquireRelease} for resource acquisition with a release finalizer * @see {@link ensuring} for attaching a finalizer to one effect * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6808,6 +6955,7 @@ export const addFinalizer: ( * output // => ["Task started", "Task completed", "Cleanup: This always runs!", 42] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6845,6 +6993,7 @@ export const ensuring: { * output // => ["Cleanup on error: TaskError: Something went wrong", Exit.fail(error)] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -6883,6 +7032,7 @@ export const onError: { * output // => ["Cause: boom", Exit.fail("boom")] * ``` * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -6911,6 +7061,7 @@ export const onErrorIf: { * @see {@link onErrorIf} for selecting failures with a boolean predicate * @see {@link onExitFilter} for selecting from every exit instead of only failures * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -6944,6 +7095,7 @@ export const onErrorFilter: { * * @see {@link onExit} for ordinary exit-aware cleanup whose finalizer always returns an effect * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -6980,6 +7132,7 @@ export const onExitPrimitive: ( * output // => ["Task succeeded with: 42", 42] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -7016,6 +7169,7 @@ export const onExit: { * output // => ["Succeeded with: 42", 42] * ``` * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -7049,6 +7203,7 @@ export const onExitIf: { * @see {@link onExitIf} for selecting exits with a boolean predicate * @see {@link onErrorFilter} for selecting only failure causes * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -7120,6 +7275,7 @@ export const onExitFilter: { * time-to-live duration for the cached value. * @see {@link cachedInvalidateWithTTL} for a similar function that includes an * additional effect for manually invalidating the cached value. + * @stability stable * @category caching * @since 2.0.0 */ @@ -7206,6 +7362,7 @@ export const cached: (self: Effect) => Effect> * indefinitely. * @see {@link cachedInvalidateWithTTL} for a similar function that includes an * additional effect for manually invalidating the cached value. + * @stability stable * @category caching * @since 2.0.0 */ @@ -7278,6 +7435,7 @@ export const cachedWithTTL: { * indefinitely. * @see {@link cachedWithTTL} for a similar function that caches the result for * a specified duration but does not include an effect for manual invalidation. + * @stability stable * @category caching * @since 2.0.0 */ @@ -7315,6 +7473,7 @@ export const cachedInvalidateWithTTL: { * Effect.runSyncExit(program)._tag // => "Failure" * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7334,6 +7493,7 @@ export const interrupt: Effect = internal.interrupt * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7362,6 +7522,7 @@ export const interruptible: ( * output // => ["Task was interrupted, cleaning up..."] * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7395,6 +7556,7 @@ export const onInterrupt: { * output // => ["Starting critical section...", "Critical section completed"] * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7430,6 +7592,7 @@ export const uninterruptible: ( * output // => ["Uninterruptible phase...", "Interruptible phase...", "Back to uninterruptible"] * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7467,6 +7630,7 @@ export const uninterruptibleMask: ( * output // => ["Interruptible phase...", "Uninterruptible phase...", "Back to interruptible"] * ``` * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -7497,6 +7661,7 @@ export const interruptibleMask: ( * * @see {@link scoped} for binding resource lifetime to a scope * + * @stability stable * @category interruption * @since 4.0.0 */ @@ -7509,6 +7674,7 @@ export const abortSignal: Effect = internal.abortSign /** * Type helpers for repeating effects. * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7573,6 +7739,7 @@ export declare namespace Repeat { * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7667,6 +7834,7 @@ export const forever: < * @see {@link retry} for failure-based repetition * @see {@link repeatOrElse} for fallback handling when repetition fails * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7741,6 +7909,7 @@ export const repeat: { * output // => ["Attempt 1 failed", "Final failure: Error 1, after 0 attempts", 0] * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7771,6 +7940,7 @@ export const repeatOrElse: { * @see {@link all} for running the returned effects and collecting results * @see {@link replicateEffect} for repeating an effect and collecting results in one step with concurrency and discard options * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7806,6 +7976,7 @@ export const replicate: { * output // => [[1, 1, 1]] * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7867,6 +8038,7 @@ export const replicateEffect: { * @see {@link scheduleFrom} for a variant that allows the schedule's decision * to depend on the result of this effect. * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7918,6 +8090,7 @@ export const schedule: { * output // => ["Processing: 0", "Processing: 0", 2] * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -7953,6 +8126,7 @@ export const scheduleFrom: { * Effect.runSync(program) // => "function" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -7974,6 +8148,7 @@ export const tracer: Effect = internal.tracer * Effect.runSync(program) // => "completed" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8003,6 +8178,7 @@ export const withTracer: { * Effect.runSync(program) // => 42 * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8027,6 +8203,7 @@ export const withTracerEnabled: { * Effect.runSync(program) // => 42 * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8058,6 +8235,7 @@ export const withTracerTiming: { * Effect.runSync(Effect.all([annotated1, annotated2])) // => ['result', 'result'] * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8100,6 +8278,7 @@ export const annotateSpans: { * Effect.runSync(traced) // => "success" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8130,6 +8309,7 @@ export const annotateCurrentSpan: { * Effect.runSync(traced) // => "my-span" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8160,6 +8340,7 @@ export const currentSpan: Effect = internal.curr * Effect.runSync(traced) // => "Span" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8186,6 +8367,7 @@ export const currentParentSpan: Effect = inte * Effect.runSync(program) // => { userId: '123', operation: 'data-processing' } * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8213,6 +8395,7 @@ export const spanAnnotations: Effect>> = intern * Effect.runSync(program).length // => 0 * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8262,6 +8445,7 @@ export const spanLinks: Effect> = internal.spanLinks * Effect.runSync(program).length // => 2 * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8299,6 +8483,7 @@ export const linkSpans: { * Effect.runSync(program) // => "my-operation" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8329,6 +8514,7 @@ export const makeSpan: (name: string, options?: SpanOptionsNoTrace) => Effect "scoped-operation" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8358,6 +8544,7 @@ export const makeSpanScoped: ( * Effect.runSync(program) // => "user-operation: success" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8382,6 +8569,7 @@ export const useSpan: { * Effect.runSync(traced) // => "result" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8423,6 +8611,7 @@ export const withSpan: { * Effect.runSync(program) // => "completed" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8457,6 +8646,7 @@ export const withSpanScoped: { * Effect.runSync(program) // => "completed" * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -8507,6 +8697,7 @@ export const withParentSpan: { * * @see {@link requestUnsafe} for the low-level entry point when you already have a `Context` and need to enqueue outside an `Effect` * + * @stability stable * @category running * @since 2.0.0 */ @@ -8534,6 +8725,7 @@ export const request: { * * @see {@link request} for the `Effect`-returning API used for normal request execution * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -8589,6 +8781,7 @@ export const requestUnsafe: ( * await Effect.runPromise(program) // => "result" * ``` * + * @stability stable * @category forking * @since 4.0.0 */ @@ -8632,6 +8825,7 @@ export const forkChild: < * await Effect.runPromise(program) // => "done" * ``` * + * @stability stable * @category forking * @since 2.0.0 */ @@ -8675,6 +8869,7 @@ export const forkIn: { * await Effect.runPromise(program) // => "scope completed" * ``` * + * @stability stable * @category forking * @since 2.0.0 */ @@ -8715,6 +8910,7 @@ export const forkScoped: < * await Effect.runPromise(program) // => "daemon result" * ``` * + * @stability stable * @category forking * @since 4.0.0 */ @@ -8758,6 +8954,7 @@ export const forkDetach: < * @see {@link forkIn} for forking into an explicit scope * @see {@link forkScoped} for forking fibers tied to the current scope * + * @stability stable * @category forking * @since 2.0.0 */ @@ -8781,6 +8978,7 @@ export const awaitAllChildren: (self: Effect) => Effect ["number"] * ``` * + * @stability stable * @category accessors * @since 4.0.0 */ @@ -8798,6 +8996,7 @@ export const fiber: Effect> = internal.fiber * Effect.runSync(program) // => "number" * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -8827,6 +9026,7 @@ export const fiberId: Effect = internal.fiberId * @see {@link runPromise} for promise-based running with these options * @see {@link runPromiseExit} for promise-based running that returns an `Exit` * + * @stability stable * @category running * @since 4.0.0 */ @@ -8863,6 +9063,7 @@ export interface RunOptions { * output // => ["running...", "done"] * ``` * + * @stability stable * @category running * @since 2.0.0 */ @@ -8904,6 +9105,7 @@ export const runFork: (effect: Effect, options?: RunOptions | * output // => ["Hello from service!", "done"] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -8956,6 +9158,7 @@ export const runForkWith: ( * output // => ["Started", "Success"] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -9003,6 +9206,7 @@ export const runCallbackWith: ( * output // => ["working", "success: done"] * ``` * + * @stability stable * @category running * @since 2.0.0 */ @@ -9045,6 +9249,7 @@ export const runCallback: ( * ``` * * @see {@link runPromiseExit} for a version that returns an `Exit` type instead of rejecting. + * @stability stable * @category running * @since 2.0.0 */ @@ -9084,6 +9289,7 @@ export const runPromise: ( * await Effect.runPromiseWith(context)(program) // => "Connecting to https://api.example.com" * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -9120,6 +9326,7 @@ export const runPromiseWith: ( * * @see {@link runPromise} for a version that rejects on failure. * + * @stability stable * @category running * @since 2.0.0 */ @@ -9164,6 +9371,7 @@ export const runPromiseExit: ( * output // => ["Success: Result for: SELECT * FROM users"] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -9225,6 +9433,7 @@ export const runPromiseExitWith: ( * * @see {@link runSyncExit} for a version that returns an `Exit` type instead of * throwing an error. + * @stability stable * @category running * @since 2.0.0 */ @@ -9262,6 +9471,7 @@ export const runSync: (effect: Effect) => A = internal.runSync * result // => 5 * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -9313,6 +9523,7 @@ export const runSyncWith: ( * * @see {@link runSync} for a version that throws on failure. * + * @stability stable * @category running * @since 2.0.0 */ @@ -9358,6 +9569,7 @@ export const runSyncExit: (effect: Effect) => Exit.Exit = inte * output // => ["[LOG] Computing result...", "Success: 42"] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -9376,6 +9588,7 @@ export const runSyncExitWith: ( * * Use these to describe generator-based signatures and traced or untraced variants. * + * @stability stable * @category constructors * @since 3.11.0 */ @@ -13550,6 +13763,7 @@ export declare namespace fn { * Effect.runSync(program) // => "hello" * ``` * + * @stability stable * @category constructors * @since 3.12.0 */ @@ -13674,6 +13888,7 @@ export const fnUntraced: fn.Untraced = internal.fnUntraced * Effect.runSync(program) // => "hello" * ``` * + * @stability stable * @category constructors * @since 3.11.0 */ @@ -13703,6 +13918,7 @@ export const fn: fn.Traced & { * Effect.runSync(program) // => "Clock is available" * ``` * + * @stability stable * @category accessors * @since 2.0.0 */ @@ -13744,6 +13960,7 @@ export const clockWith: ( * output // => ["Warn: Cache miss"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13773,6 +13990,7 @@ export const logWithLevel: (level?: Severity) => (...message: ReadonlyArray * output // => ["Info: Result: 4", 4] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13799,6 +14017,7 @@ export const log: (...message: ReadonlyArray) => Effect = internal.lo * output // => ["Fatal: Critical system failure"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13824,6 +14043,7 @@ export const logFatal: (...message: ReadonlyArray) => Effect = intern * output // => ["Warn: API rate limit approaching"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13849,6 +14069,7 @@ export const logWarning: (...message: ReadonlyArray) => Effect = inte * output // => ["Error: Database connection failed"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13874,6 +14095,7 @@ export const logError: (...message: ReadonlyArray) => Effect = intern * output // => ["Info: Application starting up"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13903,6 +14125,7 @@ export const logInfo: (...message: ReadonlyArray) => Effect = interna * output // => ["Debug: Debug mode enabled"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13932,6 +14155,7 @@ export const logDebug: (...message: ReadonlyArray) => Effect = intern * output // => ["Trace: Entering function processData"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -13963,6 +14187,7 @@ export const logTrace: (...message: ReadonlyArray) => Effect = intern * output // => ["[CUSTOM]: This will go to both default and custom logger"] * ``` * + * @stability stable * @category logging * @since 4.0.0 */ @@ -14016,6 +14241,7 @@ export const withLogger: { * output // => ["Starting operation", "Starting operation"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -14089,6 +14315,7 @@ export const annotateLogs: { * * @see {@link annotateLogs} for annotating one effect * + * @stability stable * @category logging * @since 3.1.0 */ @@ -14129,6 +14356,7 @@ export const annotateLogsScoped: { * output // => ["Making HTTP request", "Connecting to database", "Executing query", "Processing results", "Sending response", "data"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -14192,6 +14420,7 @@ export const withLogSpan: { * Effect.runSync(Metric.value(exitTracker)).occurrences.get("success") // => 1 * ``` * + * @stability stable * @category metrics * @since 4.0.0 */ @@ -14267,6 +14496,7 @@ export const track: { * Effect.runSync(Metric.value(requestSizeGauge)).value // => 12 * ``` * + * @stability stable * @category metrics * @since 4.0.0 */ @@ -14344,6 +14574,7 @@ export const trackSuccesses: { * Effect.runSync(Metric.value(errorTypeFrequency)).occurrences.get("ConnectionFailedError") // => 1 * ``` * + * @stability stable * @category metrics * @since 4.0.0 */ @@ -14422,6 +14653,7 @@ export const trackErrors: { * Effect.runSync(Metric.value(defectTypeFrequency)).occurrences.get("Error") // => 1 * ``` * + * @stability stable * @category metrics * @since 4.0.0 */ @@ -14492,6 +14724,7 @@ export const trackDefects: { * Effect.runSync(Metric.value(durationGauge)).value // => 1 * ``` * + * @stability stable * @category metrics * @since 4.0.0 */ @@ -14564,6 +14797,7 @@ export const trackDuration: { * Effect.runSync(runnable) // => "Transaction complete" * ``` * + * @stability stable * @category transactions * @since 4.0.0 */ @@ -14625,6 +14859,7 @@ export class Transaction extends Context.Service< * output // => ["Transaction sum: 30", "Final ref1: 10", "Final ref2: 20"] * ``` * + * @stability stable * @category transactions * @since 4.0.0 */ @@ -14633,7 +14868,7 @@ export const tx = ( ): Effect> => withFiber((fiber) => { let state = Context.getOrUndefined(fiber.context, Transaction) - if (state) { + if (state && !completedTransactions.has(state)) { return effect as Effect> } // Create transaction state only at the outermost boundary @@ -14646,8 +14881,11 @@ export const tx = ( body: constant( restore(effect).pipe( provideService(Transaction, state), - tapCause(() => { + tapCause((cause) => { if (!state.retry) return void_ + // txRetry interrupts the body; any other reason in the cause is a real failure. + // Roll back now so the step below fails instead of waiting or rerunning. + if (!internal.hasInterruptsOnly(cause)) return sync(() => clearTransaction(state)) return restore(awaitPendingTransaction(state)) }), exit @@ -14659,9 +14897,9 @@ export const tx = ( } if (Exit.isSuccess(exit)) { commitTransaction(fiber, state) - } else { - clearTransaction(state) } + clearTransaction(state) + completedTransactions.add(state) result = exit } }), @@ -14670,6 +14908,10 @@ export const tx = ( ) }) +// Child fibers inherit the boundary's state, so mark it completed once the +// boundary finishes and let later `tx` calls start a fresh boundary. +const completedTransactions = new WeakSet() + const isTransactionConsistent = (state: Transaction["Service"]) => { for (const [ref, { version }] of state.journal) { if (ref.version !== version) { @@ -14756,6 +14998,7 @@ function clearTransaction(state: Transaction["Service"]) { * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category transactions * @since 4.0.0 */ @@ -14769,6 +15012,7 @@ export const txRetry: Effect = flatMap( /** * Type helpers for converting callback-based functions into `Effect` functions. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15065,6 +15309,7 @@ type EffectifyArgs) => any> = Parameters exten * error.message // => "Failed to process hello: unavailable" * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15086,7 +15331,7 @@ export const effectify: { try { fn(...args, (err: globalThis.Error | null, result: A) => { if (err) { - resume(fail(onError ? onError(err, args) : err)) + resume(onError ? suspend(() => fail(onError(err, args))) : fail(err)) } else { resume(succeed(result)) } @@ -15126,6 +15371,7 @@ export const effectify: { * // Type 'string' is not assignable to type 'number' * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -15159,6 +15405,7 @@ export const satisfiesSuccessType = () => (effect: Effect * // Type 'string' is not assignable to type 'ValidationError' * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -15189,6 +15436,7 @@ export const satisfiesErrorType = () => (effect: Effect() => (effect: Effec * await Effect.runPromise(Effect.all([mapped, mappedPending])) // => [10, 10] * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -15270,6 +15519,7 @@ export const mapEager: { * output // => [['mapped: original error', 'mapped: error']] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -15316,6 +15566,7 @@ export const mapErrorEager: { * output // => [10, "Failed: error"] * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -15362,6 +15613,7 @@ export const mapBothEager: { * await Effect.runPromise(Effect.all([flatMapped, flatMappedPending])) // => [10, 10] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -15419,6 +15671,7 @@ export const flatMapEager: { * output // => [['recovered from: original error', 42, 'recovered from: error']] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -15455,6 +15708,7 @@ export const catchEager: { * Effect.runSync(effect) // => "computed eagerly" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Effectable.ts b/packages/effect/src/Effectable.ts similarity index 98% rename from repos/effect/packages/effect/src/Effectable.ts rename to packages/effect/src/Effectable.ts index b38518f65..73636f18c 100644 --- a/repos/effect/packages/effect/src/Effectable.ts +++ b/packages/effect/src/Effectable.ts @@ -4,6 +4,7 @@ * domain-specific values, such as service keys or configuration descriptions, * be evaluated by Effect and yielded inside `Effect.gen`. * + * @stability stable * @since 4.0.0 */ import type * as Effect from "./Effect.ts" @@ -26,6 +27,7 @@ import { evaluate, makePrimitiveProto } from "./internal/core.ts" * @see {@link Class} for a class-based approach to defining custom Effect values * @see {@link Mixin} for wrapping an existing class constructor * + * @stability stable * @category prototypes * @since 4.0.0 */ @@ -64,6 +66,7 @@ const Base: new() => Effect.Effect = (() => { * * @see {@link Prototype} for a lower-level primitive approach to creating custom Effect-like values without a class * @see {@link Mixin} for wrapping an existing class constructor + * @stability stable * @category constructors * @since 2.0.0 */ @@ -123,6 +126,7 @@ declare abstract class MixinBase extends Class { * * @see {@link Prototype} for a lower-level primitive approach to creating custom Effect-like values without a class * @see {@link Class} for a base constructor to extend + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Equal.ts b/packages/effect/src/Equal.ts similarity index 98% rename from repos/effect/packages/effect/src/Equal.ts rename to packages/effect/src/Equal.ts index 5ea7eb54e..9b6801ea4 100644 --- a/repos/effect/packages/effect/src/Equal.ts +++ b/packages/effect/src/Equal.ts @@ -7,6 +7,7 @@ * comparison builders, and helpers for marking objects that should compare only * by reference. * + * @stability stable * @since 2.0.0 */ import type { Equivalence } from "./Equivalence.ts" @@ -49,6 +50,7 @@ import { hasProperty } from "./Predicate.ts" * * @see {@link Equal} — the interface that uses this symbol * @see {@link isEqual} — type guard for `Equal` implementors + * @stability stable * @category symbols * @since 2.0.0 */ @@ -103,6 +105,7 @@ export const symbol = "~effect/Equal" * @see {@link symbol} — the property key used by the equality method * @see {@link equals} — the main comparison function * @see {@link isEqual} — type guard for `Equal` implementors + * @stability stable * @category models * @since 2.0.0 */ @@ -167,6 +170,7 @@ export interface Equal extends Hash.Hash { * @see {@link Equal} — the interface for custom equality * @see {@link isEqual} — check whether a value implements `Equal` * @see {@link asEquivalence} — wrap `equals` as an `Equivalence` + * @stability stable * @category equality * @since 2.0.0 */ @@ -330,11 +334,13 @@ function compareHashed( equivalent: (self: A, that: A) => boolean ): boolean { const groups = new Map>() + let remaining = 0 for (const item of that) { const h = hashOf(item) const group = groups.get(h) if (group) group.push(item) else groups.set(h, [item]) + remaining++ } outer: for (const item of self) { const group = groups.get(hashOf(item)) @@ -343,13 +349,14 @@ function compareHashed( if (equivalent(item, group[i])) { group[i] = group[group.length - 1] group.pop() + remaining-- continue outer } } } return false } - return true + return remaining === 0 } const entryHash = (entry: readonly [unknown, unknown]): number => Hash.hash(entry[0]) @@ -410,6 +417,7 @@ export function makeCompareSet(equivalence: Equivalence) { * * @see {@link Equal} — the interface being checked * @see {@link symbol} — the property key that signals `Equal` support + * @stability stable * @category guards * @since 2.0.0 */ @@ -438,6 +446,7 @@ export const isEqual = (u: unknown): u is Equal => hasProperty(u, symbol) * ``` * * @see {@link equals} — the underlying comparison function + * @stability stable * @category instances * @since 4.0.0 */ @@ -480,6 +489,7 @@ export const asEquivalence: () => Equivalence = () => equals * @see {@link byReferenceUnsafe} — same effect without a proxy (mutates the * original) * @see {@link equals} — the comparison function affected by this opt-out + * @stability stable * @category equality * @since 4.0.0 */ @@ -523,6 +533,7 @@ export const byReference = (obj: T): T => byReferenceUnsafe(ne * * @see {@link byReference} — safer alternative that creates a proxy * @see {@link equals} — the comparison function affected by this opt-out + * @stability stable * @category unsafe * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Equivalence.ts b/packages/effect/src/Equivalence.ts similarity index 98% rename from repos/effect/packages/effect/src/Equivalence.ts rename to packages/effect/src/Equivalence.ts index e1fe8ce5d..ecfd537fb 100644 --- a/repos/effect/packages/effect/src/Equivalence.ts +++ b/packages/effect/src/Equivalence.ts @@ -7,6 +7,7 @@ * helpers for tuples, arrays, structs, records, dates, and values compared * through a derived field. * + * @stability stable * @since 2.0.0 */ import { dual } from "./Function.ts" @@ -54,6 +55,7 @@ import * as Reducer from "./Reducer.ts" * * @see {@link make} * @see {@link strictEqual} + * @stability stable * @category models * @since 2.0.0 */ @@ -89,6 +91,7 @@ export type Equivalence = (self: A, that: A) => boolean * * @see {@link Equivalence} * @see {@link TypeLambda} + * @stability stable * @category utility types * @since 2.0.0 */ @@ -141,6 +144,7 @@ export interface EquivalenceTypeLambda extends TypeLambda { * * @see {@link strictEqual} * @see {@link mapInput} + * @stability stable * @category constructors * @since 2.0.0 */ @@ -193,6 +197,7 @@ const isStrictEquivalent = (x: unknown, y: unknown) => x === y * * @see {@link make} * @see `Equal` for structural equality + * @stability stable * @category constructors * @since 4.0.0 */ @@ -214,6 +219,7 @@ export const strictEqual: () => Equivalence = () => isStrictEquivalent * Equivalence.String("hello", "world") // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -236,6 +242,7 @@ export const String: Equivalence = isStrictEquivalent * Equivalence.Number(NaN, NaN) // => true * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -259,6 +266,7 @@ export const Number: Equivalence = make((self, that) => * Equivalence.Boolean(true, false) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -280,6 +288,7 @@ export const Boolean: Equivalence = isStrictEquivalent * Equivalence.BigInt(1n, 2n) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -331,6 +340,7 @@ export const BigInt: Equivalence = isStrictEquivalent * * @see {@link combineAll} * @see {@link mapInput} + * @stability stable * @category combining * @since 2.0.0 */ @@ -400,6 +410,7 @@ export const combine: { * * @see {@link combine} * @see {@link mapInput} + * @stability stable * @category combining * @since 2.0.0 */ @@ -470,6 +481,7 @@ export const combineAll = (collection: Iterable>): Equivalence * * @see {@link combine} * @see {@link Struct} + * @stability stable * @category mapping * @since 2.0.0 */ @@ -535,6 +547,7 @@ export const mapInput: { * customTupleEq(["Hello", "World", "Test"], ["HELLO", "WORLD", "TEST"]) // => true * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -614,6 +627,7 @@ export { * * @see {@link Tuple} * @see {@link Record} + * @stability stable * @category combinators * @since 4.0.0 */ @@ -685,6 +699,7 @@ export { * @see {@link Record} * @see {@link mapInput} * @see {@link combine} + * @stability stable * @category combinators * @since 4.0.0 */ @@ -749,6 +764,7 @@ export function Struct>>( * numberRecordEq(scores1, scores3) // => false * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -803,6 +819,7 @@ export function Record(value: Equivalence): Equivalence() { * @see {@link Number} for the numeric equivalence applied to each `Date#getTime()` result * @see {@link mapInput} for deriving an equivalence by mapping inputs before comparison * @see {@link strictEqual} for reference equality when two values must be the same object + * @stability stable * @category instances * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/ErrorReporter.ts b/packages/effect/src/ErrorReporter.ts similarity index 95% rename from repos/effect/packages/effect/src/ErrorReporter.ts rename to packages/effect/src/ErrorReporter.ts index 220c141e0..8c5fbcae6 100644 --- a/repos/effect/packages/effect/src/ErrorReporter.ts +++ b/packages/effect/src/ErrorReporter.ts @@ -8,6 +8,7 @@ * layers for installing reporters and symbols for marking errors as ignored or * attaching severity and attributes. * + * @stability unstable * @since 4.0.0 */ import type * as Cause from "./Cause.ts" @@ -30,6 +31,7 @@ import type * as Scope from "./Scope.ts" * * Use to refer to the runtime type identifier type in low-level integrations. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -44,6 +46,7 @@ export type TypeId = "~effect/ErrorReporter" * implementations. Most code should create reporters with `make` and register * them with `layer`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -70,6 +73,7 @@ export const TypeId: TypeId = "~effect/ErrorReporter" * @see {@link report} for manually reporting a `Cause` * @see {@link Effect.withErrorReporting} for reporting failures from an effect * + * @stability unstable * @category services * @since 4.0.0 */ @@ -120,6 +124,7 @@ export interface ErrorReporter { * @see {@link layer} for registering reporters in the environment * @see {@link report} for manually reporting a `Cause` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -170,6 +175,7 @@ export const make = ( * Use when you need to read or replace the current set of error reporters * directly. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -228,6 +234,7 @@ export const CurrentErrorReporters: Context.Reference * @see {@link make} for creating an `ErrorReporter` from a callback * @see {@link CurrentErrorReporters} for low-level access to the current reporters * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -284,6 +291,7 @@ export const layer = < * output // => "fallback value" * ``` * + * @stability unstable * @category logging * @since 4.0.0 */ @@ -315,12 +323,31 @@ export const report = (cause: Cause.Cause): Effect.Effect => * @see {@link attributes} for the runtime annotation key that attaches reporter * metadata * + * @stability unstable * @category annotations * @since 4.0.0 */ export interface Reportable { + /** + * Suppress reports for this error. + * + * @stability unstable + * @since 4.0.0 + */ readonly [ignore]?: boolean + /** + * Override the severity level for this error. + * + * @stability unstable + * @since 4.0.0 + */ readonly [severity]?: Severity + /** + * Attach extra key/value metadata to this error. + * + * @stability unstable + * @since 4.0.0 + */ readonly [attributes]?: ReadonlyRecord } @@ -343,6 +370,7 @@ declare global { * from being forwarded to reporters. This is useful for expected failures such * as HTTP 404 responses. * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -379,6 +407,7 @@ export type ignore = "~effect/ErrorReporter/ignore" * @see {@link Reportable} for the annotation contract recognized on object * errors * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -395,6 +424,7 @@ export const ignore: ignore = "~effect/ErrorReporter/ignore" * * @see {@link ignore} for the annotation key this predicate reads * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -414,6 +444,7 @@ export const isIgnored = (u: unknown): boolean => * When set to a valid `LogLevel.Severity`, the reporter callback receives this * value as `severity`. Missing or invalid values fall back to `"Info"`. * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -448,6 +479,7 @@ export type severity = "~effect/ErrorReporter/severity" * @see {@link Reportable} for the annotation contract recognized on object * errors * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -465,6 +497,7 @@ export const severity: severity = "~effect/ErrorReporter/severity" * @see {@link severity} for the annotation key used to override severity * @see {@link Reportable} for the annotation properties recognized on object errors * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -489,6 +522,7 @@ export const getSeverity = (error: object): Severity => { * include contextual information such as user IDs, request IDs, or other * domain-specific debugging data. * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -530,6 +564,7 @@ export type attributes = "~effect/ErrorReporter/attributes" * @see {@link Reportable} for the annotation contract recognized on object * errors * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -557,6 +592,7 @@ export const attributes: attributes = "~effect/ErrorReporter/attributes" * @see {@link attributes} for the annotation key used to attach metadata * @see {@link Reportable} for the annotation properties recognized on object errors * + * @stability unstable * @category annotations * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ExecutionPlan.ts b/packages/effect/src/ExecutionPlan.ts similarity index 85% rename from repos/effect/packages/effect/src/ExecutionPlan.ts rename to packages/effect/src/ExecutionPlan.ts index 135385f76..318e17de9 100644 --- a/repos/effect/packages/effect/src/ExecutionPlan.ts +++ b/packages/effect/src/ExecutionPlan.ts @@ -7,6 +7,7 @@ * order until the workflow succeeds or the plan is exhausted. This module also * supports merging plans and reading metadata for the active step and attempt. * + * @stability unstable * @since 3.16.0 */ import type { NonEmptyReadonlyArray } from "./Array.ts" @@ -20,12 +21,13 @@ import * as Layer from "./Layer.ts" import type { Pipeable } from "./Pipeable.ts" import { pipeArguments } from "./Pipeable.ts" import * as Predicate from "./Predicate.ts" -import type * as Schedule from "./Schedule.ts" +import * as Schedule from "./Schedule.ts" /** * String literal type used as the runtime type identifier for `ExecutionPlan` * values. * + * @stability unstable * @category type IDs * @since 3.16.0 */ @@ -35,6 +37,7 @@ export type TypeId = "~effect/ExecutionPlan" * Runtime type identifier attached to `ExecutionPlan` values and used by * `isExecutionPlan`. * + * @stability unstable * @category type IDs * @since 3.16.0 */ @@ -58,6 +61,7 @@ export const TypeId: TypeId = "~effect/ExecutionPlan" * @see {@link make} for constructing execution plans that satisfy this guard * @see {@link TypeId} for the runtime marker checked by this guard * + * @stability unstable * @category guards * @since 3.16.0 */ @@ -84,6 +88,7 @@ export const isExecutionPlan = (u: unknown): u is ExecutionPlan => Predicat * ThePlan.steps.map((step) => step.attempts ?? 1) // => [2, 1] * ``` * + * @stability unstable * @category models * @since 3.16.0 */ @@ -104,7 +109,9 @@ export interface ExecutionPlan< readonly while?: | ((input: Config["input"]) => Effect.Effect) | undefined - readonly schedule?: Schedule.Schedule | undefined + readonly schedule?: + | Schedule.Schedule + | undefined }> /** @@ -129,9 +136,10 @@ export interface ExecutionPlan< * * `provides` tracks services supplied by plan steps, `input` tracks the error * input consumed by schedules and `while` predicates, `error` tracks failures - * from plan layers or predicates, and `requirements` tracks services needed to - * build or run the plan. + * from plan layers, predicates, or schedules, and `requirements` tracks + * services needed to build or run the plan. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -163,6 +171,7 @@ export type ConfigBase = { * ThePlan.steps.length // => 2 * ``` * + * @stability unstable * @category constructors * @since 3.16.0 */ @@ -171,14 +180,8 @@ export const make = >( ): ExecutionPlan<{ provides: make.StepProvides input: make.StepInput - error: - | (Steps[number]["provide"] extends Context.Context | Layer.Layer ? E - : never) - | (Steps[number]["while"] extends (input: infer _I) => Effect.Effect ? _E : never) - requirements: - | (Steps[number]["provide"] extends Layer.Layer ? R : never) - | (Steps[number]["while"] extends (input: infer _I) => Effect.Effect ? R : never) - | (Steps[number]["schedule"] extends Schedule.Schedule ? R : never) + error: make.StepError + requirements: make.StepRequirements }> => makeProto(steps.map((options, i) => { if (options.attempts !== undefined && options.attempts < 1) { @@ -201,6 +204,7 @@ export const make = >( /** * Namespace containing type helpers used by `ExecutionPlan.make`. * + * @stability unstable * @since 3.16.0 */ export declare namespace make { @@ -220,9 +224,31 @@ export declare namespace make { readonly provide: Context.Context | Context.Context | Layer.Any readonly attempts?: number | undefined readonly while?: ((input: any) => boolean | Effect.Effect) | undefined - readonly schedule?: Schedule.Schedule | undefined + readonly schedule?: Schedule.Schedule | undefined } + /** + * Computes the errors from a step's layer, predicate, and schedule. + * + * @category utility types + * @since 4.0.0 + */ + export type StepError = + | (Step extends { readonly provide: Layer.Layer } ? E : never) + | (Step extends { readonly while?: (input: any) => infer Result } ? Effect.Error : never) + | (Step extends { readonly schedule?: infer S } ? Schedule.Error : never) + + /** + * Computes the services required by a step's layer, predicate, and schedule. + * + * @category utility types + * @since 4.0.0 + */ + export type StepRequirements = + | (Step extends { readonly provide: Layer.Layer } ? R : never) + | (Step extends { readonly while?: (input: any) => infer Result } ? Effect.Services : never) + | (Step extends { readonly schedule?: infer S } ? Schedule.Env : never) + /** * Computes the intersection of services provided by a list of execution-plan * steps. @@ -265,7 +291,7 @@ export declare namespace make { & Out & ( & (Step extends { readonly while: (input: infer I) => infer _ } ? I : unknown) - & (Step extends { readonly schedule: Schedule.Schedule } ? I : unknown) + & (Step extends { readonly schedule: Schedule.Schedule } ? I : unknown) ) > : Out @@ -292,6 +318,17 @@ const Proto: Omit, "steps"> = { while: step.while ? (input: any) => effect.provideContext(step.while!(input), context) : undefined, + schedule: step.schedule + ? Schedule.fromStep( + effect.provideContext( + effect.map( + Schedule.toStep(step.schedule), + (next) => (now: number, input: any) => effect.provideContext(next(now, input), context) + ), + context + ) + ) + : undefined, provide: Layer.isLayer(step.provide) ? Layer.provide(step.provide, Layer.succeedContext(context)) : step.provide @@ -331,6 +368,7 @@ const makeProto = ( * * @see {@link make} for building a plan from individual steps instead of combining existing plans * + * @stability unstable * @category combining * @since 3.16.0 */ @@ -351,6 +389,7 @@ export const merge = ("effect/ExecutionPlan * 1-based attempt number within the current step, and `stepIndex` is the * 0-based index of the step being attempted. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -406,6 +447,7 @@ export interface AttemptStart { * A successful attempt completes the plan, so this is always the final event. * `duration` is the elapsed time of the attempt. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -427,6 +469,7 @@ export interface AttemptSuccess { * afterwards is decided by the step's `attempts`, `while`, and `schedule`; a * following `AttemptStart` indicates another attempt was made. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -448,6 +491,7 @@ export interface AttemptFailure { * `AttemptSuccess` or `AttemptFailure`. An interrupted attempt emits * `AttemptFailure` with the interruption cause. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Exit.ts b/packages/effect/src/Exit.ts similarity index 97% rename from repos/effect/packages/effect/src/Exit.ts rename to packages/effect/src/Exit.ts index 889d81d48..00f9f46c9 100644 --- a/repos/effect/packages/effect/src/Exit.ts +++ b/packages/effect/src/Exit.ts @@ -7,6 +7,7 @@ * Effect results need to be inspected, transformed, filtered, or matched * synchronously as data. * + * @stability stable * @since 2.0.0 */ import type * as Cause from "./Cause.ts" @@ -53,6 +54,7 @@ const TypeId = core.ExitTypeId * @see {@link Failure} for the failure case * @see {@link match} for pattern matching * + * @stability stable * @category models * @since 2.0.0 */ @@ -66,6 +68,7 @@ export type Exit = Success | Failure * Use to reference helper types that describe the shared structure of `Exit` * values. * + * @stability stable * @since 2.0.0 */ export declare namespace Exit { @@ -112,6 +115,7 @@ export declare namespace Exit { * @see {@link isSuccess} to narrow an Exit to Success * @see {@link Failure} for the failure counterpart * + * @stability stable * @category models * @since 2.0.0 */ @@ -148,6 +152,7 @@ export interface Success extends Exit.Proto { * @see {@link isFailure} to narrow an Exit to Failure * @see {@link Success} for the success counterpart * + * @stability stable * @category models * @since 2.0.0 */ @@ -182,6 +187,7 @@ export interface Failure extends Exit.Proto { * @see {@link isSuccess} to check for a successful Exit * @see {@link isFailure} to check for a failed Exit * + * @stability stable * @category guards * @since 2.0.0 */ @@ -210,6 +216,7 @@ export const isExit: (u: unknown) => u is Exit = core.isExit * @see {@link fail} to create a failed Exit * @see {@link void_ void} for a pre-allocated success with no value * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -240,6 +247,7 @@ export const succeed: (a: A) => Exit = core.exitSucceed * @see {@link fail} to create a Failure from a plain error value * @see {@link die} to create a Failure from a defect * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -270,6 +278,7 @@ export const failCause: (cause: Cause.Cause) => Exit = core.exit * @see {@link die} to create a Failure from an unexpected defect * @see {@link failCause} to create a Failure from a full Cause * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -301,6 +310,7 @@ export const fail: (e: E) => Exit = core.exitFail * @see {@link fail} to create a Failure from a typed error * @see {@link hasDies} to check whether an Exit contains defects * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -328,6 +338,7 @@ export const die: (defect: unknown) => Exit = core.exitDie * * @see {@link hasInterrupts} to check whether an Exit contains interruptions * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -358,6 +369,7 @@ export { * @see {@link succeed} to create a success with a specific value * @see {@link asVoid} to discard the value of an existing Exit * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -387,6 +399,7 @@ export { * @see {@link isFailure} for the opposite check * @see {@link match} for exhaustive pattern matching * + * @stability stable * @category guards * @since 2.0.0 */ @@ -415,6 +428,7 @@ export const isSuccess: (self: Exit) => self is Success = effe * @see {@link isSuccess} for the opposite check * @see {@link match} for exhaustive pattern matching * + * @stability stable * @category guards * @since 2.0.0 */ @@ -445,6 +459,7 @@ export const isFailure: (self: Exit) => self is Failure = effe * @see {@link hasDies} to check for defects * @see {@link hasInterrupts} to check for interruptions * + * @stability stable * @category guards * @since 4.0.0 */ @@ -475,6 +490,7 @@ export const hasFails: (self: Exit) => self is Failure = effec * @see {@link hasFails} to check for typed errors * @see {@link hasInterrupts} to check for interruptions * + * @stability stable * @category guards * @since 4.0.0 */ @@ -505,6 +521,7 @@ export const hasDies: (self: Exit) => self is Failure = effect * @see {@link hasFails} to check for typed errors * @see {@link hasDies} to check for defects * + * @stability stable * @category guards * @since 4.0.0 */ @@ -539,6 +556,7 @@ export const hasInterrupts: (self: Exit) => self is Failure = * @see {@link filterFailure} for the inverse * @see {@link filterValue} to extract the raw value instead of the Success object * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -576,6 +594,7 @@ export const filterSuccess: ( * @see {@link filterSuccess} to get the full Success object * @see {@link getSuccess} to get the value as an Option instead * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -610,6 +629,7 @@ export const filterValue: (self: Exit) => Result.Result(self: Exit) => Result.Result(self: Exit) => Result.Result(input: Exit) => Result.Result * @see {@link findError} to find typed errors instead * @see {@link hasDies} to check for defects without extracting them * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -746,6 +769,7 @@ export const findDefect: (input: Exit) => Result.Result(self: Exit) => Exit = effect.exitAsVo * * @see {@link asVoid} to discard the value of a single Exit * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -964,6 +993,7 @@ export const asVoidAll: >>( * @see {@link getCause} to extract the Cause of a failure * @see {@link filterValue} for filter-pipeline usage * + * @stability stable * @category getters * @since 4.0.0 */ @@ -993,6 +1023,7 @@ export const getSuccess: (self: Exit) => Option = effect.exitGetS * @see {@link getSuccess} to extract the success value * @see {@link filterCause} for filter-pipeline usage * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1029,6 +1060,7 @@ export const getCause: (self: Exit) => Option> = effe * @see {@link findError} for filter-pipeline usage * @see {@link getCause} to get the full Cause as an Option * + * @stability stable * @category getters * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Fiber.ts b/packages/effect/src/Fiber.ts similarity index 98% rename from repos/effect/packages/effect/src/Fiber.ts rename to packages/effect/src/Fiber.ts index 4eae1625c..672e12429 100644 --- a/repos/effect/packages/effect/src/Fiber.ts +++ b/packages/effect/src/Fiber.ts @@ -5,6 +5,7 @@ * interrupt one or many fibers, check unknown values, access the current fiber, * and attach manually managed fibers to a `Scope` for cleanup. * + * @stability stable * @since 2.0.0 */ import type * as Arr from "./Array.ts" @@ -65,6 +66,7 @@ const TypeId = "~effect/Fiber" * actual // => Exit.succeed(42) * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -119,6 +121,7 @@ export interface Fiber extends Pipeable { * actual // => 42 * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace Fiber { @@ -216,6 +219,7 @@ export { * actual // => Exit.succeed(42) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -254,6 +258,7 @@ export { * actual // => [Exit.succeed(1), Exit.succeed(2)] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -298,6 +303,7 @@ export const awaitAll: >( * * @see {@link await_ await} for inspecting the fiber outcome as an Exit * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -324,6 +330,7 @@ export const join: (self: Fiber) => Effect = effect.fiberJoin * * @see {@link awaitAll} for collecting every fiber outcome as an Exit * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -373,6 +380,7 @@ export const joinAll: >>( * @see {@link interruptAs} for specifying the interrupting fiber ID * @see {@link await_ await} for observing the interrupted fiber's Exit * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -415,6 +423,7 @@ export const interrupt: (self: Fiber) => Effect = effect.fiber * * @see {@link interrupt} for using the current fiber as the interruptor * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -466,6 +475,7 @@ export const interruptAs: { * * @see {@link interruptAllAs} for specifying the interrupting fiber ID * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -513,6 +523,7 @@ export const interruptAll: >>( * * @see {@link interruptAll} for using the current fiber as the interruptor * + * @stability stable * @category interruption * @since 2.0.0 */ @@ -552,6 +563,7 @@ export const interruptAllAs: { * actual // => [true, false, false, false] * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -587,6 +599,7 @@ export const isFiber = ( * actual // => true * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -611,6 +624,7 @@ export const getCurrent: () => Fiber | undefined = effect.getCurrentFi * * @see {@link interrupt} for interrupting and waiting for completion * + * @stability stable * @category resource management * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/FiberHandle.ts b/packages/effect/src/FiberHandle.ts similarity index 97% rename from repos/effect/packages/effect/src/FiberHandle.ts rename to packages/effect/src/FiberHandle.ts index 3f78b4555..015862edf 100644 --- a/repos/effect/packages/effect/src/FiberHandle.ts +++ b/packages/effect/src/FiberHandle.ts @@ -8,6 +8,7 @@ * for setting, reading, clearing, and running fibers, and operations for joining * the current fiber or waiting until the handle is empty. * + * @stability stable * @since 2.0.0 */ import * as Cause from "./Cause.ts" @@ -51,6 +52,7 @@ const TypeId = "~effect/FiberHandle" * actual // => "hello" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -84,6 +86,7 @@ export interface FiberHandle extends Pipeable, * actual // => [true, false] * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -140,6 +143,7 @@ const makeUnsafe = (): FiberHandle => { * actual // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -190,6 +194,7 @@ export const make = (): Effect.Effect [Exit.failCause(Cause.interrupt(-1)), Exit.succeed("second")] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -245,6 +250,7 @@ export const makeRuntime = (): Effect.Effect< * actual // => "hello" * ``` * + * @stability stable * @category constructors * @since 3.13.0 */ @@ -301,6 +307,7 @@ const isInternalInterruption = Filter.toPredicate(Filter.compose( * actual // => "hello" * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -331,18 +338,21 @@ export const setUnsafe: { if (self.state._tag === "Closed") { fiber.interruptUnsafe(internalFiberId) return - } else if (self.state.fiber !== undefined) { - if (self.state.fiber === fiber) { + } + + const previous = self.state.fiber + if (previous !== undefined) { + if (previous === fiber) { return } else if (options?.onlyIfMissing === true) { fiber.interruptUnsafe(internalFiberId) return } - self.state.fiber.interruptUnsafe(internalFiberId) - self.state.fiber = undefined } + // Install the replacement before interruption can re-enter the handle through a finalizer. self.state.fiber = fiber + previous?.interruptUnsafe(internalFiberId) fiber.addObserver((exit) => { if (self.state._tag === "Open" && fiber === self.state.fiber) { self.state.fiber = undefined @@ -389,6 +399,7 @@ export const setUnsafe: { * actual // => "hello" * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -452,6 +463,7 @@ export const set: { * actual // => [Option.none(), Option.some(true)] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -482,6 +494,7 @@ export function getUnsafe(self: FiberHandle): Option.Option Option.some(true) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -515,6 +528,7 @@ export function get(self: FiberHandle): Effect.Effect Option.none() * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -577,6 +591,7 @@ const constInterruptedFiber = (function() { * actual // => ["hello", "world"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -672,6 +687,7 @@ const runImpl = ( * actual // => 0 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -747,6 +763,7 @@ export const runtime: ( * actual // => "hello" * ``` * + * @stability stable * @category combinators * @since 3.13.0 */ @@ -818,6 +835,7 @@ export const runtimePromise = (self: FiberHandle): () => * actual // => Exit.fail("error") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -847,6 +865,7 @@ export const join = (self: FiberHandle): Effect.Effect => * actual // => Option.none() * ``` * + * @stability stable * @category combinators * @since 3.13.0 */ diff --git a/repos/effect/packages/effect/src/FiberMap.ts b/packages/effect/src/FiberMap.ts similarity index 98% rename from repos/effect/packages/effect/src/FiberMap.ts rename to packages/effect/src/FiberMap.ts index cdda71b25..2790e50a3 100644 --- a/repos/effect/packages/effect/src/FiberMap.ts +++ b/packages/effect/src/FiberMap.ts @@ -7,6 +7,7 @@ * interrupt background work by a stable key while keeping all fibers tied to * one scope. * + * @stability stable * @since 2.0.0 */ import * as Cause from "./Cause.ts" @@ -54,6 +55,7 @@ const TypeId = "~effect/FiberMap" * actual // => 2 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -92,6 +94,7 @@ export interface FiberMap * actual // => [true, false, false] * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -156,6 +159,7 @@ const makeUnsafe = ( * actual // => 2 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -208,6 +212,7 @@ export const make = (): Effect.Effect ["Hello", "World"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -265,6 +270,7 @@ export const makeRuntime = (): Effect.Effect< * actual // => ["Hello", "World"] * ``` * + * @stability stable * @category constructors * @since 3.13.0 */ @@ -331,6 +337,7 @@ const isInternalInterruption = Filter.toPredicate(Filter.compose( * actual // => "Hello" * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -437,6 +444,7 @@ export const setUnsafe: { * actual // => "Hello" * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -500,6 +508,7 @@ export const set: { * actual // => Option.some("Hello") * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -544,6 +553,7 @@ export const getUnsafe: { * actual // => Option.some("Hello") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -578,6 +588,7 @@ export const get: { * actual // => [true, false] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -613,6 +624,7 @@ export const hasUnsafe: { * actual // => [true, false] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -651,6 +663,7 @@ export const has: { * actual // => [2, 1] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -705,6 +718,7 @@ export const remove: { * actual // => [3, 0] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -758,6 +772,7 @@ const constInterruptedFiber = (function() { * actual // => ["Hello", "World", 0] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -855,6 +870,7 @@ const runImpl = ( * actual // => [0, 0] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -932,6 +948,7 @@ export const runtime: ( * actual // => ["Hello", "World"] * ``` * + * @stability stable * @category combinators * @since 3.13.0 */ @@ -995,6 +1012,7 @@ export const runtimePromise = (self: FiberMap): () * actual // => [0, 2] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1028,6 +1046,7 @@ export const size = (self: FiberMap): Effect.Effect => * actual // => Exit.fail("error") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1059,6 +1078,7 @@ export const join = (self: FiberMap): Effect.Effect = * actual // => 0 * ``` * + * @stability stable * @category combinators * @since 3.13.0 */ diff --git a/repos/effect/packages/effect/src/FiberSet.ts b/packages/effect/src/FiberSet.ts similarity index 98% rename from repos/effect/packages/effect/src/FiberSet.ts rename to packages/effect/src/FiberSet.ts index f7cfd58db..d2a285627 100644 --- a/repos/effect/packages/effect/src/FiberSet.ts +++ b/packages/effect/src/FiberSet.ts @@ -6,6 +6,7 @@ * closes. This module includes scoped runtime constructors plus helpers for * adding, clearing, running, counting, joining, and waiting for managed fibers. * + * @stability stable * @since 2.0.0 */ import * as Cause from "./Cause.ts" @@ -50,6 +51,7 @@ const TypeId = "~effect/FiberSet" * actual // => 0 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -84,6 +86,7 @@ export interface FiberSet * actual // => [true, false] * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -148,6 +151,7 @@ const makeUnsafe = ( * actual // => 2 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -193,6 +197,7 @@ export const make = (): Effect.Effect, * actual // => ["hello", "world"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -243,6 +248,7 @@ export const makeRuntime = (): Effect.Effec * actual // => ["hello", "world"] * ``` * + * @stability stable * @category constructors * @since 3.13.0 */ @@ -300,6 +306,7 @@ const isInternalInterruption = Filter.toPredicate(Filter.compose( * actual // => 1 * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -372,6 +379,7 @@ export const addUnsafe: { * actual // => 1 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -427,6 +435,7 @@ export const add: { * actual // => [2, 0] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -473,6 +482,7 @@ const constInterruptedFiber = (function() { * actual // => ["hello", "world"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -554,6 +564,7 @@ const runImpl = ( * actual // => 0 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -624,6 +635,7 @@ export const runtime: ( * * @see {@link runtime} for a runner that returns the forked `Fiber` * + * @stability stable * @category combinators * @since 3.13.0 */ @@ -682,6 +694,7 @@ export const runtimePromise = (self: FiberSet): () => Eff * actual // => [0, 2] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -709,6 +722,7 @@ export const size = (self: FiberSet): Effect.Effect => * actual // => Exit.fail("error") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -739,6 +753,7 @@ export const join = (self: FiberSet): Effect.Effect => * actual // => 0 * ``` * + * @stability stable * @category combinators * @since 3.13.0 */ diff --git a/repos/effect/packages/effect/src/FileSystem.ts b/packages/effect/src/FileSystem.ts similarity index 98% rename from repos/effect/packages/effect/src/FileSystem.ts rename to packages/effect/src/FileSystem.ts index c737dc17f..e633dd873 100644 --- a/repos/effect/packages/effect/src/FileSystem.ts +++ b/packages/effect/src/FileSystem.ts @@ -8,6 +8,7 @@ * `PlatformError`. The module also includes file handles, open flags, watch * events, and the watch backend service. * + * @stability unstable * @since 4.0.0 */ import * as Arr from "./Array.ts" @@ -72,6 +73,7 @@ const TypeId = "~effect/FileSystem" * result.content // => "{\"env\": \"development\"}" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -411,6 +413,7 @@ export interface FileSystem { * flags // => ["r", "w", "a", "r+"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -464,6 +467,7 @@ export type OpenFlag = * Effect.runSync(withCustomFs) // => "contents" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -487,6 +491,7 @@ export const FileSystem: Context.Service = Context.Servi * @see {@link makeNoop} for a testing stub that accepts method overrides without requiring a complete implementation * @see {@link layerNoop} for providing a no-op `FileSystem` as a `Layer` in tests * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -630,6 +635,7 @@ const notFound = (method: string, path: string) => * Effect.runSync(testProgram) // => "{\"test\": true}" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -759,6 +765,7 @@ export const makeNoop = (fileSystem: Partial): FileSystem => * Effect.runSync(testProgram) // => "mocked content" * ``` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -777,6 +784,7 @@ export const layerNoop = (fileSystem: Partial): Layer.Layer hasProperty(u, FileTypeId) * result.buffer // => [1, 2, 3, 4, 5] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -878,6 +888,7 @@ export interface File { * Namespace containing types associated with open file handles, including file * descriptors, entry kinds, and stat information. * + * @stability unstable * @since 4.0.0 */ export declare namespace File { @@ -986,6 +997,7 @@ export declare namespace File { * * @see {@link File} for the open file handle API whose `seek` method consumes this mode * + * @stability unstable * @category models * @since 4.0.0 */ @@ -994,6 +1006,7 @@ export type SeekMode = "start" | "current" /** * Options for watching files or directories. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1019,6 +1032,7 @@ export interface WatchOptions { * * @see {@link FileSystem} for the service interface whose `watch` operation emits these events * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1027,6 +1041,7 @@ export type WatchEvent = WatchEvent.Create | WatchEvent.Update | WatchEvent.Remo /** * Namespace containing the concrete event shapes emitted by `FileSystem.watch`. * + * @stability unstable * @since 4.0.0 */ export declare namespace WatchEvent { @@ -1116,6 +1131,7 @@ export declare namespace WatchEvent { * Effect.runSync(withCustomBackend) // => true * ``` * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Filter.ts b/packages/effect/src/Filter.ts similarity index 97% rename from repos/effect/packages/effect/src/Filter.ts rename to packages/effect/src/Filter.ts index 631d3e03d..74977b46c 100644 --- a/repos/effect/packages/effect/src/Filter.ts +++ b/packages/effect/src/Filter.ts @@ -8,6 +8,7 @@ * filters for common JavaScript values and tags, helpers for combining filters, * and conversions to predicates, options, and results. * + * @stability stable * @since 4.0.0 */ import type { Effect } from "./Effect.ts" @@ -38,6 +39,7 @@ import type { EqualsWith, ExcludeTag, ExtractReason, ExtractTag, ReasonTags, Tag * positiveFilter(-3) // => Result.fail(-3) * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -79,6 +81,7 @@ export interface Filter { * await Effect.runPromise(validateUser("")) // => Result.fail({ id: "", isActive: false }) * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -120,6 +123,7 @@ export interface FilterEffect< * uppercaseFilter("ok") // => Result.succeed("OK") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -152,6 +156,7 @@ export const make = ( * await Effect.runPromise(asyncValidate("id")) // => Result.succeed("id") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -163,6 +168,7 @@ export const makeEffect = ( * Transforms the failure value produced by a `Filter`, leaving successful * results unchanged. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -191,6 +197,7 @@ export { * Creates a Filter that tries to apply a function and returns `fail` on * error. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -224,6 +231,7 @@ export { * isString("ok") // => Result.succeed("ok") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -237,6 +245,7 @@ export const fromPredicate: { * Creates a `Filter` from a function that returns an `Option`; `Some(value)` * passes with `value`, and `None` fails with the original input. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -256,6 +265,7 @@ export const fromPredicateOption = (predicate: (a: A) => Option.Option) * @see {@link toOption} for keeping passed values and discarding failure values * @see {@link toResult} for preserving both pass and failure values * + * @stability stable * @category converting * @since 4.0.0 */ @@ -276,6 +286,7 @@ export const toPredicate = ( * Filter.string(42) // => Result.fail(42) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -299,6 +310,7 @@ export const string: Filter = fromPredicate(Predicate.isString) * @see {@link equals} for structural equality when distinct values with equal * contents should pass * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -320,6 +332,7 @@ export const equalsStrict = * @see {@link Predicate.hasProperty} for guarding property presence instead of * calling an input's `has` method * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -347,6 +360,7 @@ export const has = * * @see {@link fromPredicate} for custom predicate-based narrowing * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -367,6 +381,7 @@ export const instanceOf = * Filter.number("42") // => Result.fail("42") * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -388,6 +403,7 @@ export const number: Filter = fromPredicate(Predicate.isNumber) * @see {@link Predicate.isBoolean} for the underlying guard * @see {@link fromPredicate} for custom predicate-based filters * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -414,6 +430,7 @@ export const boolean: Filter = fromPredicate(Predicate.isBoole * @see {@link number} for JavaScript `number` values * @see {@link Predicate.isBigInt} for the underlying guard * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -422,6 +439,7 @@ export const bigint: Filter = fromPredicate(Predicate.isBigInt) /** * A predefined filter that only passes through Symbol values. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -449,6 +467,7 @@ export const symbol: Filter = fromPredicate(Predicate.isSymbol) * @see {@link instanceOf} for constructor-based filtering * @see {@link fromPredicate} for custom date checks * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -475,6 +494,7 @@ export const date: Filter = fromPredicate(Predicate.isDate) * `Filter` result is not needed * @see {@link reason} for extracting a nested reason variant from tagged errors * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -498,6 +518,7 @@ const taggedImpl = /** * Creates a filter that extracts a reason from a tagged error. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -547,6 +568,7 @@ const reasonImpl = * equality * @see {@link Equal.equals} for the underlying structural equality semantics * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -557,6 +579,7 @@ export const equals = /** * Combines two filters with logical OR semantics. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -591,6 +614,7 @@ export const or: { * * @see {@link zip} for combining two filters into a tuple * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -637,6 +661,7 @@ export const zipWith: { * positiveAndEven(2) // => Result.succeed([2, 2]) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -671,6 +696,7 @@ export const zip: { * positiveEven(2) // => Result.succeed(2) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -706,6 +732,7 @@ export const andLeft: { * positiveDoubled(2) // => Result.succeed(4) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -741,6 +768,7 @@ export const andRight: { * stringToUpper("hello") // => Result.succeed("HELLO") * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -771,6 +799,7 @@ export const compose: { * If either filter fails, the returned filter fails with the original input * instead of the intermediate failure value. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -806,6 +835,7 @@ export const composePassthrough: { * @see {@link toResult} for keeping the filter failure value * @see {@link toPredicate} for plain boolean pass/fail checks * + * @stability stable * @category converting * @since 4.0.0 */ @@ -829,6 +859,7 @@ export const toOption = ( * @see {@link toOption} for keeping only passed values * @see {@link toPredicate} for plain boolean pass/fail checks * + * @stability stable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Formatter.ts b/packages/effect/src/Formatter.ts similarity index 87% rename from repos/effect/packages/effect/src/Formatter.ts rename to packages/effect/src/Formatter.ts index b184d5c45..3267fd539 100644 --- a/repos/effect/packages/effect/src/Formatter.ts +++ b/packages/effect/src/Formatter.ts @@ -7,6 +7,7 @@ * wraps JSON formatting with redaction and circular-reference handling, and the * module also includes helpers for property keys, paths, and dates. * + * @stability stable * @since 4.0.0 */ import * as Predicate from "./Predicate.ts" @@ -35,6 +36,7 @@ import { getRedacted, redact, symbolRedactable } from "./Redactable.ts" * * @see {@link format} * @see {@link formatJson} + * @stability stable * @category models * @since 4.0.0 */ @@ -102,6 +104,7 @@ export interface Formatter { * * @see {@link formatJson} * @see {@link Formatter} + * @stability stable * @category formatting * @since 2.0.0 */ @@ -298,6 +301,7 @@ function safeGet(input: object, key: PropertyKey): unknown { * * @see {@link format} * @see {@link Formatter} + * @stability stable * @category serialization * @since 4.0.0 */ @@ -331,8 +335,45 @@ export function formatJson(input: unknown, options?: { if (current !== redacted) { ancestors.push(current) } - return current + // Leave boxed primitives intact so JSON.stringify can unbox them natively. + if (!hasGetter(current) || isJsonPrimitiveWrapper(current)) { + return current + } + // JSON.stringify reads a getter once, then calls toJSON on the result + // before the replacer sees it. Intercept that read to redact the value + // first, instead of re-reading the getter in the replacer, which could + // repeat side effects or return a different value. + const serialized = new Proxy(current, { + get(target, key) { + const value = Reflect.get(target, key, target) + return Object.getOwnPropertyDescriptor(target, key)?.get !== undefined ? redact(value) : value + } + }) + ancestors.push(serialized) + return serialized }, options?.space ) ?? "null" } + +function hasGetter(object: object): boolean { + for (const key of Object.getOwnPropertyNames(object)) { + if (Object.getOwnPropertyDescriptor(object, key)?.get !== undefined) { + return true + } + } + return false +} + +function isJsonPrimitiveWrapper(object: object): boolean { + // Built-in valueOf checks internal slots across realms without calling user code. + for (const valueOf of [Number.prototype.valueOf, Boolean.prototype.valueOf, String.prototype.valueOf]) { + try { + Reflect.apply(valueOf, object, []) + return true + } catch { + // Try the next wrapper type. + } + } + return false +} diff --git a/repos/effect/packages/effect/src/Function.ts b/packages/effect/src/Function.ts similarity index 98% rename from repos/effect/packages/effect/src/Function.ts rename to packages/effect/src/Function.ts index edb900285..13e4e7f06 100644 --- a/repos/effect/packages/effect/src/Function.ts +++ b/packages/effect/src/Function.ts @@ -6,6 +6,7 @@ * module also contains small identity, constant, tuple, type-level, and * memoization helpers used across the library. * + * @stability stable * @since 2.0.0 */ import type { TypeLambda } from "./HKT.ts" @@ -29,6 +30,7 @@ import { pipeArguments } from "./Pipeable.ts" * // Equivalent to: (a: string) => number * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -96,6 +98,7 @@ export interface FunctionTypeLambda extends TypeLambda { * pipe(2, sum(3)) // => 5 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -177,6 +180,7 @@ export const dual: { * * @see {@link pipe} for building left-to-right pipelines * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -198,6 +202,7 @@ export const apply = (a: A) => (self: (a: A) => B): B => self(a) * constNull() // => null * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -220,6 +225,7 @@ export type LazyArg = () => A * sum(2, 3) // => 5 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -240,6 +246,7 @@ export type FunctionN, B> = (...args: A) => B * identity(5) // => 5 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -268,6 +275,7 @@ export const identity = (a: A): A => a * * @see {@link cast} for changing only the static TypeScript type * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -288,6 +296,7 @@ export const satisfies = () => (b: B) => b * * @see {@link satisfies} for checking assignability without changing the resulting type * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -312,6 +321,7 @@ export const cast: (a: A) => B = identity as any * constNull() // => null * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -332,6 +342,7 @@ export const constant = (value: A): LazyArg => () => value * Function.constTrue() // => true * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -352,6 +363,7 @@ export const constTrue: LazyArg = constant(true) * Function.constFalse() // => false * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -372,6 +384,7 @@ export const constFalse: LazyArg = constant(false) * Function.constNull() // => null * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -392,6 +405,7 @@ export const constNull: LazyArg = constant(null) * Function.constUndefined() // => undefined * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -413,6 +427,7 @@ export const constUndefined: LazyArg = constant(undefined) * Function.constVoid() // => undefined * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -436,6 +451,7 @@ export const constVoid: LazyArg = constUndefined * Function.flip(f)("aaa")(2) // => -1 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -467,6 +483,7 @@ export const flip = , B extends Array, C>( * @see {@link flow} for composing a left-to-right sequence of functions * @see {@link pipe} for applying a value through a left-to-right sequence immediately * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -499,6 +516,7 @@ export const compose: { * } * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -525,6 +543,7 @@ export const absurd = (_: never): A => { * * @see {@link untupled} for adapting a tuple-argument function back to multiple arguments * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -549,6 +568,7 @@ export const tupled = , B>(f: (...a: A) => B): * * @see {@link tupled} for adapting a multi-argument function to one tuple argument * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -607,6 +627,7 @@ export const untupled = , B>(f: (a: A) => B): ( * ) // => [6, 8] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1091,6 +1112,7 @@ export function pipe(a: unknown, ...args: Array): unknown { * @see {@link pipe} for applying a value through a left-to-right sequence immediately * @see {@link compose} for composing exactly two functions * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1284,6 +1306,7 @@ export function flow( * * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -1306,6 +1329,7 @@ export const hole: () => T = cast(absurd) * Function.SK(0, "hello") // => "hello" * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1333,6 +1357,7 @@ export const SK = (_: A, b: B): B => b * mutated after its first call, later calls still return the cached result for * that reference. * + * @stability stable * @category caching * @since 4.0.0 */ @@ -1369,6 +1394,7 @@ export function memoize(f: (a: A) => O): * different value, this memoization changes that behavior. * * @see {@link memoize} for memoizing functions without an idempotence requirement + * @stability stable * @category caching * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Graph.ts b/packages/effect/src/Graph.ts similarity index 97% rename from repos/effect/packages/effect/src/Graph.ts rename to packages/effect/src/Graph.ts index f0dbb6730..5fcbd5881 100644 --- a/repos/effect/packages/effect/src/Graph.ts +++ b/packages/effect/src/Graph.ts @@ -6,6 +6,7 @@ * both nodes and edges. The module includes traversal, analysis, path-finding, * transformation, and diagram export utilities. * + * @stability unstable * @since 4.0.0 */ @@ -42,6 +43,7 @@ const TypeId = internal.TypeId * * @see {@link EdgeIndex} for edge identifiers instead of node identifiers * + * @stability unstable * @category models * @since 3.18.0 */ @@ -62,6 +64,7 @@ export type NodeIndex = number * * @see {@link NodeIndex} for node identifiers instead of edge identifiers * + * @stability unstable * @category models * @since 3.18.0 */ @@ -75,6 +78,7 @@ export type EdgeIndex = number * Use as the graph edge value that carries source node, target node, and stored * edge data together. * + * @stability unstable * @category models * @since 3.18.0 */ @@ -92,6 +96,7 @@ export interface Edge { * Use when writing graph-polymorphic types or helpers that need to preserve * whether a graph is directed or undirected. * + * @stability unstable * @category models * @since 3.18.0 */ @@ -100,6 +105,7 @@ export type Kind = "directed" | "undirected" /** * A node and its stable index in a graph snapshot. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -111,6 +117,7 @@ export interface IndexedNode { /** * An edge and its stable index in a graph snapshot. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -137,6 +144,7 @@ export interface IndexedEdge extends Edge { * reconstruction, new identifiers continue after the greatest active index. * * @see {@link fromSnapshot} for reconstructing a graph + * @stability unstable * @category models * @since 4.0.0 */ @@ -161,6 +169,7 @@ export interface Snapshot { * * @see {@link MutableGraph} for the mutable counterpart used inside mutation scopes * + * @stability unstable * @category models * @since 3.18.0 */ @@ -175,6 +184,7 @@ export interface Graph /** * Companion namespace containing type-level metadata for immutable graphs. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -208,6 +218,7 @@ export declare namespace Graph { * @see {@link Graph} for the immutable graph interface * @see {@link mutate} for scoped mutation of an immutable graph * + * @stability unstable * @category models * @since 3.18.0 */ @@ -222,6 +233,7 @@ export interface MutableGraph /** * Companion namespace containing type-level metadata for scoped mutable graphs. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -261,6 +273,7 @@ const copyEdge = (edge: Edge): Edge => ({ * @see {@link directed} for constructing directed graphs * @see {@link UndirectedGraph} for graphs whose edges connect both endpoints * + * @stability unstable * @category models * @since 3.18.0 */ @@ -281,6 +294,7 @@ export type DirectedGraph = Graph * @see {@link undirected} for constructing undirected graphs * @see {@link DirectedGraph} for graphs whose edges have source-to-target direction * + * @stability unstable * @category models * @since 3.18.0 */ @@ -297,6 +311,7 @@ export type UndirectedGraph = Graph * @see {@link MutableGraph} for the generic mutable graph type * @see {@link MutableUndirectedGraph} for mutable graphs without edge direction * + * @stability unstable * @category models * @since 3.18.0 */ @@ -313,6 +328,7 @@ export type MutableDirectedGraph = MutableGraph * @see {@link MutableDirectedGraph} for mutable graphs with directed edges * @see {@link MutableGraph} for the generic mutable graph type * + * @stability unstable * @category models * @since 3.18.0 */ @@ -334,6 +350,7 @@ export type MutableUndirectedGraph = MutableGraph * Use when handling failures thrown by graph operations that reject invalid * graph structure or unsupported algorithm inputs. * + * @stability unstable * @category errors * @since 3.18.0 */ @@ -398,6 +415,7 @@ const withMutationGuard = ( * distinguish immutable graphs from mutable graphs or directed graphs from * undirected graphs. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -436,6 +454,7 @@ export const isGraph: (snapshot: Snapshot): * ``` * * @see {@link fromSnapshot} for reconstructing an immutable graph + * @stability unstable * @category converting * @since 4.0.0 */ @@ -568,6 +588,7 @@ export const toSnapshot = ( * * @see {@link directed} for constructing a directed graph directly * @see {@link undirected} for constructing an undirected graph directly + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -614,6 +635,7 @@ export const make = * Array.of(Graph.nodeCount(graph), Graph.edgeCount(graph)) // => [3, 2] * ``` * + * @stability unstable * @category constructors * @since 3.18.0 */ @@ -650,6 +672,7 @@ export const directed: ( * Array.of(Graph.nodeCount(graph), Graph.edgeCount(graph)) // => [3, 2] * ``` * + * @stability unstable * @category constructors * @since 3.18.0 */ @@ -688,6 +711,7 @@ export const undirected: ( * * @see {@link endMutation} for finalizing the mutable graph * @see {@link mutate} for automatically scoped mutation + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -724,6 +748,7 @@ export const beginMutation = ( * * @see {@link beginMutation} for opening a manual mutation scope * @see {@link mutate} for automatically scoped mutation + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -795,6 +820,7 @@ const mutateScoped = ( * * @see {@link beginMutation} for opening a manual mutation scope * @see {@link endMutation} for finalizing a manual mutation scope + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -887,6 +913,7 @@ class EdgeIdentity implements Equal.Equal { * with the same endpoint identities and projected edge identity are treated as * the same member by graph set operations. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1019,6 +1046,7 @@ const assertSameKind = (self: Graph, that: Graph): * ``` * * @see {@link sum} for combining graphs without merging equal nodes + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1116,6 +1144,7 @@ export const compose: { * ``` * * @see {@link compose} for identity-based graph union + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1220,6 +1249,7 @@ export const intersection: { * ``` * * @see {@link symmetricDifference} for retaining edges unique to either graph + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1313,6 +1343,7 @@ export const difference: { * ``` * * @see {@link difference} for removing only the edges found in another graph + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1404,6 +1435,7 @@ export const symmetricDifference: { * ``` * * @see {@link hasEdge} for testing one relationship + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1457,6 +1489,7 @@ export const complement: { * `direction` controls how directed edges are traversed and defaults to * `"outgoing"`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1506,6 +1539,7 @@ export interface NeighborhoodConfig { * ``` * * @see {@link inducedSubgraph} for selecting nodes while preserving identifiers + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1572,6 +1606,7 @@ export const neighborhood: { * * @see {@link neighborhood} for selecting nodes by traversal distance * + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1631,6 +1666,7 @@ export const inducedSubgraph: { * * @see {@link compose} for merging overlapping logical nodes by identity * + * @stability unstable * @category set operations * @since 4.0.0 */ @@ -1698,6 +1734,7 @@ export const sum: { * @see {@link mutate} for obtaining a mutable graph from an immutable graph * @see {@link addEdge} for connecting existing nodes * + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -1739,6 +1776,7 @@ export const addNode = ( * Graph.getNode(graph, 0) // => Option.some("Node A") * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -1774,6 +1812,7 @@ export const getNode: { * Graph.hasNode(graph, 999) // => false * ``` * + * @stability unstable * @category predicates * @since 3.18.0 */ @@ -1805,6 +1844,7 @@ export const hasNode: { * Graph.nodeCount(graphWithNodes) // => 3 * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -1830,6 +1870,7 @@ export const nodeCount = ( * Graph.findNode(graph, (data) => data === "Node D") // => Option.none() * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -1874,6 +1915,7 @@ export const findNode: { * Graph.findNodes(graph, (data) => data === "Not Found") // => [] * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -1921,6 +1963,7 @@ export const findNodes: { * Graph.findEdge(graph, (data) => data > 100) // => Option.none() * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -1968,6 +2011,7 @@ export const findEdge: { * Graph.findEdges(graph, (data) => data > 100) // => [] * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -2022,6 +2066,7 @@ export const findEdges: { * Graph.getNode(graph, 0) // => Option.some("NODE A") * ``` * + * @stability unstable * @category transforming * @since 3.18.0 */ @@ -2070,6 +2115,7 @@ export const updateNode = ( * Option.map(Graph.getEdge(result, 0), (edge) => edge.data) // => Option.some(20) * ``` * + * @stability unstable * @category transforming * @since 3.18.0 */ @@ -2129,6 +2175,7 @@ export const updateEdge = ( * * @see {@link updateNode} for updating one node * @see {@link filterMapNodes} for mapping while removing nodes + * @stability unstable * @category mapping * @since 3.18.0 */ @@ -2182,6 +2229,7 @@ export const mapNodes = ( * * @see {@link updateEdge} for updating one edge * @see {@link filterMapEdges} for mapping while removing edges + * @stability unstable * @category mapping * @since 3.18.0 */ @@ -2236,6 +2284,7 @@ export const mapEdges = ( * Option.map(Graph.getEdge(graph, 0), (edge) => edge.source) // => Option.some(1) * ``` * + * @stability unstable * @category transforming * @since 3.18.0 */ @@ -2296,6 +2345,7 @@ export const reverse = ( * Graph.nodeCount(graph) // => 2 * ``` * + * @stability unstable * @category filtering * @since 3.18.0 */ @@ -2355,6 +2405,7 @@ export const filterMapNodes = ( * Graph.edgeCount(graph) // => 2 * ``` * + * @stability unstable * @category filtering * @since 3.18.0 */ @@ -2415,6 +2466,7 @@ export const filterMapEdges = ( * Graph.nodeCount(graph) // => 2 * ``` * + * @stability unstable * @category filtering * @since 3.18.0 */ @@ -2469,6 +2521,7 @@ export const filterNodes = ( * Graph.edgeCount(graph) // => 2 * ``` * + * @stability unstable * @category filtering * @since 3.18.0 */ @@ -2554,6 +2607,7 @@ const invalidateCycleFlagOnAddition = ( * @see {@link mutate} for obtaining a mutable graph from an immutable graph * @see {@link addNode} for creating node indexes before connecting them * + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -2639,6 +2693,7 @@ export const addEdge = ( * Array.of(Graph.nodeCount(result), Graph.edgeCount(result)) // => [1, 0] * ``` * + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -2671,6 +2726,7 @@ export const removeNode = ( * * @see {@link removeNode} for removing one node * + * @stability unstable * @category mutations * @since 4.0.0 */ @@ -2764,6 +2820,7 @@ const removeNodeInternal = ( * Array.of(Graph.nodeCount(result), Graph.edgeCount(result)) // => [2, 0] * ``` * + * @stability unstable * @category mutations * @since 3.18.0 */ @@ -2797,6 +2854,7 @@ export const removeEdge = ( * * @see {@link removeEdge} for removing one edge * + * @stability unstable * @category mutations * @since 4.0.0 */ @@ -2899,6 +2957,7 @@ const removeEdgeInternal = ( * Graph.getEdge(graph, 0) // => Option.some({ source: 0, target: 1, data: 42 }) * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -2947,6 +3006,7 @@ export const getEdge: { * ``` * * @see {@link edgesBetween} for all matching edge identifiers + * @stability unstable * @category predicates * @since 3.18.0 */ @@ -3008,6 +3068,7 @@ export const hasEdge: { * Graph.edgeCount(graphWithEdges) // => 3 * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -3023,6 +3084,7 @@ export const edgeCount = ( * Each edge is returned once in graph edge order, including self-loops. * Throws a `GraphError` when the node does not exist. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3087,6 +3149,7 @@ export const incidentEdges: { * Parallel edges and self-loops are returned separately in adjacency order. * Throws a `GraphError` for an undirected graph or missing node. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3120,6 +3183,7 @@ export const outgoingEdges: { * Parallel edges and self-loops are returned separately in reverse-adjacency * order. Throws a `GraphError` for an undirected graph or missing node. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3154,6 +3218,7 @@ export const incomingEdges: { * graphs include either stored orientation. Parallel edges are retained. * Throws a `GraphError` when either node does not exist. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3202,6 +3267,7 @@ export const edgesBetween: { * Parallel edges count separately and a self-loop contributes two. Throws a * `GraphError` for a directed graph or missing node. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3232,6 +3298,7 @@ export const degree: { * Parallel edges count separately and a self-loop contributes one. Throws a * `GraphError` for an undirected graph or missing node. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3262,6 +3329,7 @@ export const outDegree: { * Parallel edges count separately and a self-loop contributes one. Throws a * `GraphError` for an undirected graph or missing node. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3388,6 +3456,7 @@ const ScanDegreeLimit = 32 * Graph.neighbors(graph, 1) // => [] * ``` * + * @stability unstable * @category getters * @since 3.18.0 */ @@ -3430,6 +3499,7 @@ export const neighbors: { * @see {@link predecessors} for incoming neighbors in a directed graph * @see {@link neighbors} for generic neighbor lookup across graph kinds * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3470,6 +3540,7 @@ export const successors: { * @see {@link successors} for outgoing neighbors in a directed graph * @see {@link neighbors} for generic neighbor lookup across graph kinds * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -3530,6 +3601,7 @@ export const predecessors: { * @deprecated Use {@link successors} for outgoing neighbors or {@link predecessors} for incoming neighbors. * @see {@link successors} for outgoing neighbors in a directed graph * @see {@link predecessors} for incoming neighbors in a directed graph + * @stability unstable * @category getters * @since 3.18.0 */ @@ -3591,6 +3663,7 @@ export const neighborsDirected: { * ``` * * @see {@link toGraphViz} for generating DOT output + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -3643,6 +3716,7 @@ const escapeGraphVizString = (value: string): string => * ``` * * @see {@link toMermaid} for Mermaid diagram output + * @stability unstable * @category converting * @since 3.18.0 */ @@ -3730,6 +3804,7 @@ export const toGraphViz: { * options.nodeShape?.("decision") // => "diamond" * ``` * + * @stability unstable * @category models * @since 3.18.0 */ @@ -3776,6 +3851,7 @@ export type MermaidNodeShape = * Array.of(horizontalOptions.direction, verticalOptions.direction, bottomUpOptions.direction) // => ["LR", "TB", "BT"] * ``` * + * @stability unstable * @category models * @since 3.18.0 */ @@ -3818,6 +3894,7 @@ export type MermaidDirection = * Array.of(flowchartOptions.diagramType, graphOptions.diagramType, autoOptions.diagramType) // => ["flowchart", "graph", undefined] * ``` * + * @stability unstable * @category models * @since 3.18.0 */ @@ -3861,6 +3938,7 @@ export type MermaidDiagramType = * ``` * * @see {@link toMermaid} for generating Mermaid output + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -3979,6 +4057,7 @@ const formatMermaidNode = ( * ``` * * @see {@link toGraphViz} for GraphViz DOT output + * @stability unstable * @category converting * @since 3.18.0 */ @@ -4052,6 +4131,7 @@ export const toMermaid: { * `"outgoing"` selects edges whose source is the node, while `"incoming"` * selects edges whose target is the node. * + * @stability unstable * @category models * @since 3.18.0 */ @@ -4084,6 +4164,7 @@ export type Direction = "outgoing" | "incoming" * Array.from(Graph.indices(Graph.bfs(graph, { start: [1], direction: "undirected" }))) // => [1, 0, 2] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -4101,6 +4182,7 @@ export type TraversalDirection = Direction | "undirected" * `path` repeats its first node at the end, so `edges.length` is always * `path.length - 1`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -4123,6 +4205,7 @@ export interface CycleResult { * one-edge cycle, and two parallel undirected edges form a two-edge cycle. * * @see {@link isAcyclic} when only a boolean cycle check is needed + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -4226,6 +4309,7 @@ export const findCycle = ( * * @see {@link findCycle} for retrieving one cycle witness * @see {@link topo} for ordering a directed acyclic graph + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -4359,6 +4443,7 @@ export const isAcyclic = ( * ``` * * @see {@link maximumBipartiteMatching} for matching nodes after validation + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -4410,6 +4495,7 @@ export const isBipartite = ( * `left` and `right` refer to the bipartition derived by * `maximumBipartiteMatching`, not to the stored edge orientation. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -4494,6 +4580,7 @@ const bipartiteColors = ( * ``` * * @see {@link isBipartite} for validating the graph without computing a matching + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -4670,6 +4757,7 @@ const getTraversableNeighbor = ( * * `direction` defaults to `"outgoing"` and is ignored for undirected graphs. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -4745,6 +4833,7 @@ const getUnweightedDistances = ( * @see {@link bfs} for lazy traversal in increasing hop distance * @see {@link dijkstra} for weighted shortest paths * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -4783,6 +4872,7 @@ export const unweightedDistances: { * @see {@link unweightedDistances} for hop distances to all reachable nodes * @see {@link dijkstra} for a minimum-cost path * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -4893,6 +4983,7 @@ export const hasPath: { * @see {@link isConnected} when only a boolean connectivity check is needed * @see {@link weaklyConnectedComponents} for directed graphs with orientation ignored * @see {@link stronglyConnectedComponents} for mutual directed reachability + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -5116,6 +5207,7 @@ const analyzeLowLinks = ( * * @see {@link articulationPoints} for single-node failure points * @see {@link biconnectedComponents} for maximal regions without an articulation split + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5156,6 +5248,7 @@ export const bridges = ( * * @see {@link bridges} for single-edge failure points * @see {@link biconnectedComponents} for the regions joined at articulation points + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5203,6 +5296,7 @@ export const articulationPoints = ( * * @see {@link articulationPoints} for the nodes shared between components * @see {@link bridges} for edges whose removal disconnects the graph + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5227,6 +5321,7 @@ export const biconnectedComponents = ( * * The source and target must be distinct existing nodes in a directed graph. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -5244,6 +5339,7 @@ export interface MaximumFlowConfig { * `flows` contains every original edge, including zero-flow edges. `cut` * contains the crossing edge identifiers of the corresponding minimum cut. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -5261,6 +5357,7 @@ export interface MaximumFlowResult { * `source` contains nodes residual-reachable from the configured source and * `target` contains the remaining nodes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -5456,6 +5553,7 @@ const solveMaximumFlow = ( * ``` * * @see {@link minimumCut} for the residual-reachability partition + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5511,6 +5609,7 @@ export const maximumFlow: { * ``` * * @see {@link maximumFlow} for per-edge flow values + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5555,6 +5654,7 @@ export const minimumCut: { * @see {@link isWeaklyConnected} when only a boolean check is needed * @see {@link stronglyConnectedComponents} for mutual directed reachability * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5640,6 +5740,7 @@ export const weaklyConnectedComponents = ( * * @see {@link isStronglyConnected} when only a boolean check is needed * @see {@link weaklyConnectedComponents} when edge orientation should be ignored + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -5773,6 +5874,7 @@ const csrReachesAll = ( * * @see {@link connectedComponents} for the component partition * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -5801,6 +5903,7 @@ export const isConnected = ( * * @see {@link isStronglyConnected} when edge orientation must be respected * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -5830,6 +5933,7 @@ export const isWeaklyConnected = ( * * @see {@link isWeaklyConnected} when edge orientation should be ignored * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -5856,6 +5960,7 @@ export const isStronglyConnected = ( * The empty graph is not a tree. Parallel edges and self-loops prevent a graph * from being a tree. Throws a `GraphError` when used with a directed graph. * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -5890,6 +5995,7 @@ export const isTree = ( * Throws a `GraphError` for a directed graph or when a weight is `NaN` or * `-Infinity`. Edges weighted `Infinity` are omitted. * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -5994,6 +6100,7 @@ export const minimumSpanningForest: { * coalesced by retaining the first edge for each required pair. Throws a * `GraphError` for an undirected graph or cyclic input. * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -6085,6 +6192,7 @@ export const transitiveReduction = ( * `costs` contains original edge data, not the numeric output of the cost * function unless the edge data is numeric. * + * @stability unstable * @category models * @since 3.18.0 */ @@ -6241,6 +6349,7 @@ const denseMinHeapPop = (heap: DenseMinHeap): boolean => { * `dijkstra` throws a `GraphError` when either endpoint does not exist or when * the cost function returns a negative weight or `NaN`. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -6296,6 +6405,7 @@ export interface DijkstraConfig { * @see {@link astar} when a useful heuristic can guide the search * @see {@link bellmanFord} when edge costs may be negative * @see {@link floydWarshall} when shortest paths are needed for all pairs + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -6429,6 +6539,7 @@ export const dijkstra: { * source and target node indices. Unreachable pairs have distance `Infinity`, * path `null`, and empty edge and cost arrays. * + * @stability unstable * @category models * @since 3.18.0 */ @@ -6445,14 +6556,16 @@ export interface AllPairsResult { * * **When to use** * - * Use when many or all node pairs will be queried and cubic computation plus - * quadratic result storage is acceptable. + * Use when many or all node pairs will be queried and cubic distance computation + * plus storage for reconstructed paths is acceptable. * * **Details** * * Computes distances, reconstructed node paths, and edge-data paths for every - * source and target pair in O(V^3) time. Negative edge weights are allowed, and - * `Infinity` behaves like an impassable edge. + * source and target pair. Distance computation takes O(V^3) time. Negative edge + * weights are allowed, and `Infinity` behaves like an impassable edge. Immutable + * path witnesses can require O(V^3) working storage; reconstructed paths have + * repeated nodes removed. * * **Gotchas** * @@ -6481,6 +6594,7 @@ export interface AllPairsResult { * * @see {@link dijkstra} for one query with non-negative edge costs * @see {@link bellmanFord} for one query that may include negative edge costs + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -6501,13 +6615,15 @@ export const floydWarshall: { const edgeIds = csr.getEdgeIds(cache) const edgeCache = csr.getEdgeEndpoints(cache) const size = cache.nodeIds.length - // Flat matrices keep the O(N^2) working set contiguous and avoid nested map lookups in the O(N^3) loop. + // Immutable witnesses retain the actual walks used by relaxation. Following + // independently updated next-hop rows can create cycles through rounding. + type Witness = + | { readonly target: number; readonly edge: number } + | { readonly target: number; readonly left: Witness; readonly right: Witness } + const witnesses: Array = new Array(size * size) + // The flat distance matrix avoids nested map lookups in the O(N^3) loop. const distancesMatrix = new Float64Array(size * size) - const nextMatrix = new Int32Array(size * size) - const edgeMatrix = new Int32Array(size * size) distancesMatrix.fill(Infinity) - nextMatrix.fill(-1) - edgeMatrix.fill(-1) for (let i = 0; i < size; i++) { distancesMatrix[i * size + i] = 0 } @@ -6523,15 +6639,13 @@ export const floydWarshall: { const position = source * size + target if (weight < distancesMatrix[position]) { distancesMatrix[position] = weight - nextMatrix[position] = target - edgeMatrix[position] = edge + witnesses[position] = { target, edge } } if (graph.type === "undirected") { const reverse = target * size + source if (weight < distancesMatrix[reverse]) { distancesMatrix[reverse] = weight - nextMatrix[reverse] = source - edgeMatrix[reverse] = edge + witnesses[reverse] = { target: source, edge } } } } @@ -6545,7 +6659,7 @@ export const floydWarshall: { if (distanceIK === Infinity) { continue } - const nextIK = nextMatrix[iRow + k] + const left = witnesses[iRow + k] for (let j = 0; j < size; j++) { const distanceKJ = distancesMatrix[kRow + j] if (distanceKJ === Infinity) { @@ -6555,9 +6669,14 @@ export const floydWarshall: { if (!Number.isFinite(candidate)) { throw new GraphError({ message: "Floyd-Warshall distance calculation exceeded the finite number range" }) } - if (candidate < distancesMatrix[iRow + j] && nextIK !== -1) { + if (candidate < distancesMatrix[iRow + j]) { distancesMatrix[iRow + j] = candidate - nextMatrix[iRow + j] = nextIK + const right = witnesses[kRow + j] + witnesses[iRow + j] = left === undefined + ? right + : right === undefined + ? left + : { target: j, left, right } } } } @@ -6573,6 +6692,9 @@ export const floydWarshall: { const paths = new Map | null>>() const edgePaths = new Map>>() const costs = new Map>>() + // Reuse scratch storage; erased suffixes and completed paths clear only touched entries. + const positions = new Int32Array(size) + positions.fill(-1) for (let i = 0; i < size; i++) { const source = cache.nodeIds[i] const distanceRow = new Map() @@ -6600,19 +6722,35 @@ export const floydWarshall: { const path = [source] const pathEdges: Array = [] const pathCosts: Array = [] - let current = i - while (current !== j) { - const next = nextMatrix[current * size + j] - if (next === -1) { - break - } - const edge = edgeMatrix[current * size + next] - if (edge !== -1) { - pathEdges.push(edgeIds[edge]) - pathCosts.push(edges[edge].data) + positions[i] = 0 + const nodes = [i] + const stack = [witnesses[i * size + j]!] + // Witnesses only reference older witnesses, so this traversal cannot + // follow a cyclic pointer chain. Erase closed walks rather than return + // a truncated route or reject a valid zero-cost cycle. + while (stack.length > 0) { + const witness = stack.pop()! + const position = positions[witness.target] + if (position !== -1) { + for (let p = position + 1; p < nodes.length; p++) { + positions[nodes[p]] = -1 + } + nodes.length = position + 1 + path.length = position + 1 + pathEdges.length = position + pathCosts.length = position + } else if ("edge" in witness) { + positions[witness.target] = nodes.length + nodes.push(witness.target) + path.push(cache.nodeIds[witness.target]) + pathEdges.push(edgeIds[witness.edge]) + pathCosts.push(edges[witness.edge].data) + } else { + stack.push(witness.right, witness.left) } - current = next - path.push(cache.nodeIds[current]) + } + for (const node of nodes) { + positions[node] = -1 } pathRow.set(target, path) edgePathRow.set(target, pathEdges) @@ -6643,6 +6781,7 @@ export const floydWarshall: { * Heuristic values must be finite and the heuristic must be consistent for A* * to guarantee a shortest path. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -6705,6 +6844,7 @@ export interface AstarConfig { * * @see {@link dijkstra} when no useful heuristic is available * @see {@link bellmanFord} when edge costs may be negative + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -6855,6 +6995,7 @@ export const astar: { * Specifies the source and target node indices, plus a cost function that maps * each edge's data to a numeric weight. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -6909,6 +7050,7 @@ export interface BellmanFordConfig { * ``` * * @see {@link dijkstra} for non-negative edge costs + * @stability unstable * @category algorithms * @since 3.18.0 */ @@ -7077,6 +7219,7 @@ export const bellmanFord: { * * Each fresh iterator repeats the path enumeration. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -7097,6 +7240,7 @@ export interface PathWalker extends Iterable> {} * * `limit` must be a non-negative integer or `Infinity`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -7122,6 +7266,7 @@ export interface SimplePathsConfig { * * Invalid costs and limits throw a `GraphError` when evaluated. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -7162,6 +7307,7 @@ const pathWalker = (iterator: () => Iterator>): PathWalker = * * @see {@link allShortestPaths} for enumerating only minimum-cost routes * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -7276,6 +7422,7 @@ export const simplePaths: { * @see {@link dijkstra} when one shortest path is sufficient * @see {@link simplePaths} for routes regardless of cost * + * @stability unstable * @category algorithms * @since 4.0.0 */ @@ -7449,6 +7596,7 @@ export const allShortestPaths: { * Array.from(Graph.entries(allNodes)) // => [[0, "A"], [1, "B"]] * ``` * + * @stability unstable * @category models * @since 3.18.0 */ @@ -7546,6 +7694,7 @@ const traversalStartPositions = (cache: csr.Csr, start: ReadonlyArray * @see {@link Walker} for the generic lazy iterator wrapper * @see {@link EdgeWalker} for edge iterators * + * @stability unstable * @category models * @since 3.18.0 */ @@ -7565,6 +7714,7 @@ export type NodeWalker = Walker * @see {@link NodeWalker} for node iterators * @see {@link edges} for creating edge walkers * + * @stability unstable * @category models * @since 3.18.0 */ @@ -7588,6 +7738,7 @@ export type EdgeWalker = Walker> * Array.from(Graph.indices(dfs)) // => [0, 1] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -7611,6 +7762,7 @@ export const indices = (walker: Walker): Iterable => walker.visit * Array.from(Graph.values(dfs)) // => ["A", "B"] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -7634,6 +7786,7 @@ export const values = (walker: Walker): Iterable => walker.visit( * Array.from(Graph.entries(dfs)) // => [[0, "A"], [1, "B"]] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -7664,6 +7817,7 @@ export const entries = (walker: Walker): Iterable<[T, N]> => * revalidates those starts against the graph snapshot it captures. Later * mutations are not observed by an active iterator. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -7715,6 +7869,7 @@ export interface SearchConfig { * * @see {@link bfs} for traversal in increasing hop distance * @see {@link dfsPostOrder} for emitting descendants before ancestors + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -7892,6 +8047,7 @@ export const dfs: { * * @see {@link dfs} for branch-first traversal * @see {@link unweightedDistances} for collecting hop counts + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -8020,6 +8176,7 @@ export const bfs: { * * Throws a `GraphError` when any initial node has incoming edges. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -8064,6 +8221,7 @@ export interface TopoConfig { * ``` * * @see {@link isAcyclic} for checking the required graph property + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -8204,6 +8362,7 @@ export const topo: { * ``` * * @see {@link dfs} for emitting nodes when first visited + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -8346,6 +8505,7 @@ export const dfsPostOrder: { * Array.from(Graph.indices(Graph.nodes(graph))) // => [0, 1, 2] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -8399,6 +8559,7 @@ export const nodes = ( * Array.from(Graph.indices(Graph.edges(graph))) // => [0, 1] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ @@ -8438,6 +8599,7 @@ export const edges = ( * nodes with no incoming edges. If omitted, `direction` defaults to * `"outgoing"`. * + * @stability unstable * @category configuration * @since 3.18.0 */ @@ -8485,6 +8647,7 @@ export interface ExternalsConfig { * Array.from(Graph.indices(Graph.externals(graph, { direction: "incoming" }))) // => [0, 3] * ``` * + * @stability unstable * @category iterators * @since 3.18.0 */ diff --git a/repos/effect/packages/effect/src/HKT.ts b/packages/effect/src/HKT.ts similarity index 98% rename from repos/effect/packages/effect/src/HKT.ts rename to packages/effect/src/HKT.ts index ae7a5608d..374c37fb7 100644 --- a/repos/effect/packages/effect/src/HKT.ts +++ b/packages/effect/src/HKT.ts @@ -7,6 +7,7 @@ * mostly useful when defining generic helpers or type classes that should work * across several data types. * + * @stability stable * @since 2.0.0 */ import type * as Types from "./Types.ts" @@ -47,6 +48,7 @@ import type * as Types from "./Types.ts" * const value: HKT.Kind, never, never, never, string> = identity.of("ok") * ``` * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -89,6 +91,7 @@ export declare const URI: unique symbol * const witness: keyof Monad = "flatMap" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -134,6 +137,7 @@ export interface TypeClass { * const witness: HKT.Kind = ["ok"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -199,6 +203,7 @@ export interface TypeLambda { * const witness: OptionString = Option.some("ok") * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Hash.ts b/packages/effect/src/Hash.ts similarity index 98% rename from repos/effect/packages/effect/src/Hash.ts rename to packages/effect/src/Hash.ts index a66c55a0e..cf018c46a 100644 --- a/repos/effect/packages/effect/src/Hash.ts +++ b/packages/effect/src/Hash.ts @@ -7,6 +7,7 @@ * reference-based hashes, plus functions for combining and optimizing numeric * hash values. * + * @stability stable * @since 2.0.0 */ import { dual } from "./Function.ts" @@ -26,6 +27,7 @@ import { hasProperty } from "./Predicate.ts" * @see {@link isHash} for checking whether a value implements `Hash` * @see {@link hash} for computing hash values * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -59,6 +61,7 @@ export const symbol = "~effect/Hash" * new MyClass(42)[Hash.symbol]() // => 42 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -98,6 +101,7 @@ export interface Hash { * Hash.hash([1, 2, 3]) === Hash.hash([1, 2, 3]) // => true * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -191,6 +195,7 @@ export const hash: (self: A) => number = (self: A) => { * typeof Hash.random(obj2) // => "number" * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -237,6 +242,7 @@ const mix = (h: number): number => { * @see {@link hash} for computing hash values from arbitrary inputs * @see {@link structureKeys} for hashing selected object fields without manual combination * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -265,6 +271,7 @@ export const combine: { * Hash.optimize(1234567890) // => 160826066 * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -298,6 +305,7 @@ export const optimize = (n: number): number => (n & 0xbfffffff) | ((n >>> 1) & 0 * Hash.isHash("string") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -329,6 +337,7 @@ const float64 = new DataView(new ArrayBuffer(8)) * Hash.number(100) === Hash.number(100) // => true * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -366,6 +375,7 @@ export const number = (n: number) => { * Hash.string("test") === Hash.string("test") // => true * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -408,6 +418,7 @@ export const string = (str: string) => { * hash1 === hash3 // => true * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -446,6 +457,7 @@ export const structureKeys = (o: object, keys: Iterable) => { * Hash.structure(obj1) === Hash.structure(obj3) // => true * ``` * + * @stability stable * @category hashing * @since 2.0.0 */ @@ -490,6 +502,7 @@ const unordered = (seed: number, f: (el: any) => number) => (iter: Iterable * * @see {@link hash} for the general-purpose hash dispatcher * + * @stability stable * @category hashing * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/HashMap.ts b/packages/effect/src/HashMap.ts similarity index 97% rename from repos/effect/packages/effect/src/HashMap.ts rename to packages/effect/src/HashMap.ts index 7a9e80837..60e5be154 100644 --- a/repos/effect/packages/effect/src/HashMap.ts +++ b/packages/effect/src/HashMap.ts @@ -7,6 +7,7 @@ * batch updates. This module also includes constructors, iteration, conversion, * mapping, filtering, and reducing helpers. * + * @stability stable * @since 2.0.0 */ @@ -44,6 +45,7 @@ const TypeId = internal.HashMapTypeId * HashMap.set(map, "d", 4) // => HashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -85,6 +87,7 @@ export interface HashMap extends Iterable<[Key, Value]>, Equ * updatedInventory // => HashMap.make(["laptop", { quantity: 5, price: 999 }], ["mouse", { quantity: 20, price: 29 }], ["tablet", newProduct]) * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace HashMap { @@ -221,6 +224,7 @@ export declare namespace HashMap { * HashMap.isHashMap(null) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -240,6 +244,7 @@ export const isHashMap: { * HashMap.empty() // => HashMap.empty() * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -256,6 +261,7 @@ export const empty: () => HashMap = internal.empty * HashMap.make(["a", 1], ["b", 2], ["c", 3]) // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -278,6 +284,7 @@ export const make: >( * HashMap.fromIterable(entries) // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -298,6 +305,7 @@ export const fromIterable: (entries: Iterable) => HashMap * HashMap.isEmpty(nonEmptyMap) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -321,6 +329,7 @@ export const isEmpty: (self: HashMap) => boolean = internal.isEmpty * HashMap.get("b")(map) // => Option.some(2) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -354,6 +363,7 @@ export const get: { * HashMap.getHash(userMap, "user999", Hash.string("user999")) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -397,6 +407,7 @@ export const getHash: { * // Error: "HashMap.getUnsafe: key not found" * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -422,6 +433,7 @@ export const getUnsafe: { * HashMap.has("b")(map) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -457,6 +469,7 @@ export const has: { * HashMap.hasHash(userMap, "Admin", lowercaseHash) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -478,6 +491,7 @@ export const hasHash: { * HashMap.hasBy(hm, (value) => value === "b") // => false * ``` * + * @stability stable * @category predicates * @since 3.16.0 */ @@ -502,6 +516,7 @@ export const hasBy: { * map1 // => HashMap.make(["a", 1]) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -522,6 +537,7 @@ export const set: { * Array.from(HashMap.keys(map)).sort() // => ["a", "b", "c"] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -539,6 +555,7 @@ export const keys: (self: HashMap) => IterableIterator = internal * Array.from(HashMap.values(map)).sort() // => [1, 2, 3] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -569,6 +586,7 @@ export const values: (self: HashMap) => IterableIterator = intern * allEmployees.filter((emp) => emp.department === "engineering").length // => 2 * ``` * + * @stability stable * @category getters * @since 3.13.0 */ @@ -600,6 +618,7 @@ export const toValues = (self: HashMap): Array => Array.from(valu * Array.from(HashMap.entries(config)).length // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -633,6 +652,7 @@ export const entries: (self: HashMap) => IterableIterator<[K, V]> = * HashMap.fromIterable(scoreEntries) // => HashMap.make(["alice", 1250], ["charlie", 1100], ["bob", 980]) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -653,6 +673,7 @@ export const toEntries = (self: HashMap): Array<[K, V]> => Array.fro * HashMap.size(map) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -685,6 +706,7 @@ export const size: (self: HashMap) => number = internal.size * HashMap.endMutation(mutable) // => HashMap.make(["b", 2], ["c", 3]) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -713,6 +735,7 @@ export const beginMutation: (self: HashMap) => HashMap = inter * HashMap.endMutation(mutable) // => HashMap.make(["y", 20], ["z", 30], ["w", 40]) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -740,6 +763,7 @@ export const endMutation: (self: HashMap) => HashMap = interna * map2 // => HashMap.make(["a", 1], ["b", 2], ["c", 3]) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -772,6 +796,7 @@ export const mutate: { * HashMap.get(updated, "a") // => Option.some(2) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -827,6 +852,7 @@ export const modifyAt: { * HashMap.get(withClicks, "clicks") // => Option.some(1) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -850,6 +876,7 @@ export const modifyHash: { * HashMap.get(map2, "b") // => Option.some(2) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -879,6 +906,7 @@ export const modify: { * HashMap.get(union, "b") // => Option.some(20) * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -902,6 +930,7 @@ export const union: { * map2 // => HashMap.make(["a", 1], ["c", 3]) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -924,6 +953,7 @@ export const remove: { * map2 // => HashMap.make(["a", 1], ["c", 3]) * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -948,6 +978,7 @@ export const removeMany: { * HashMap.get(map2, "a") // => Option.some(10) * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -971,6 +1002,7 @@ export const setMany: { * HashMap.get(map2, "b") // => Option.some("b:4") * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1001,6 +1033,7 @@ export const map: { * HashMap.get(map2, "b2") // => Option.some(4) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1027,6 +1060,7 @@ export const flatMap: { * collected.sort() // => [["a", 1], ["b", 2]] * ``` * + * @stability stable * @category traversing * @since 2.0.0 */ @@ -1047,6 +1081,7 @@ export const forEach: { * HashMap.reduce(map, 0, (acc, value) => acc + value) // => 6 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -1069,6 +1104,7 @@ export const reduce: { * map2 // => HashMap.make(["b", 2], ["d", 4]) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1096,6 +1132,7 @@ export const filter: { * HashMap.get(map2, "a") // => Option.some(1) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1120,6 +1157,7 @@ export const compact: (self: HashMap>) => HashMap = int * HashMap.get(map2, "b") // => Option.some(4) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1141,6 +1179,7 @@ export const filterMap: { * HashMap.findFirst(map, (value, key) => key === "b" && value > 1) // => Option.some(["b", 2]) * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -1163,6 +1202,7 @@ export const findFirst: { * HashMap.some(map, (value) => value > 5) // => false * ``` * + * @stability stable * @category predicates * @since 3.13.0 */ @@ -1185,6 +1225,7 @@ export const some: { * HashMap.every(map, (value) => value > 1) // => false * ``` * + * @stability stable * @category predicates * @since 3.14.0 */ diff --git a/repos/effect/packages/effect/src/HashRing.ts b/packages/effect/src/HashRing.ts similarity index 98% rename from repos/effect/packages/effect/src/HashRing.ts rename to packages/effect/src/HashRing.ts index 2288eea34..1711de55d 100644 --- a/repos/effect/packages/effect/src/HashRing.ts +++ b/packages/effect/src/HashRing.ts @@ -7,6 +7,7 @@ * can create rings, add or remove nodes by `PrimaryKey`, route an input string * to a node, and compute shard assignments. * + * @stability unstable * @since 4.0.0 */ import { dual } from "./Function.ts" @@ -34,6 +35,7 @@ const TypeId = "~effect/HashRing" as const * Nodes are identified by their `PrimaryKey` value and can be iterated from the * ring. * + * @stability unstable * @category models * @since 3.19.0 */ @@ -66,6 +68,7 @@ export interface HashRing extends Pipeable, Ite * @see {@link HashRing} for the type narrowed by this guard * @see {@link make} for creating an empty `HashRing` * + * @stability unstable * @category guards * @since 3.19.0 */ @@ -87,6 +90,7 @@ export const isHashRing = (u: unknown): u is HashRing => hasProperty(u, Typ * @see {@link add} for registering one node after creation * @see {@link addMany} for registering several nodes after creation * + * @stability unstable * @category constructors * @since 3.19.0 */ @@ -124,6 +128,7 @@ const Proto = { * * Use to register or update several nodes in a `HashRing` at the same weight. * + * @stability unstable * @category combinators * @since 3.19.0 */ @@ -211,6 +216,7 @@ function updateTotalWeight(self: HashRing) { * @see {@link remove} for unregistering a node * @see {@link has} for checking primary-key membership * + * @stability unstable * @category combinators * @since 3.19.0 */ @@ -245,6 +251,7 @@ export const add: { * @see {@link add} for registering or updating a node * @see {@link has} for checking membership by primary key * + * @stability unstable * @category combinators * @since 3.19.0 */ @@ -279,6 +286,7 @@ export const remove: { * @see {@link remove} for removing nodes by the same primary-key identity * @see {@link get} for routing an input string to a node * + * @stability unstable * @category combinators * @since 3.19.0 */ @@ -302,6 +310,7 @@ export const has: { * @see {@link getShards} for assigning fixed shard indexes instead of routing * one input string at a time * + * @stability unstable * @category combinators * @since 3.19.0 */ @@ -327,6 +336,7 @@ export const get = (self: HashRing, input: s * Finite fractional values of `count` are rounded down. `NaN` and non-positive * values produce an empty shard distribution. * + * @stability unstable * @category combinators * @since 3.19.0 */ diff --git a/repos/effect/packages/effect/src/HashSet.ts b/packages/effect/src/HashSet.ts similarity index 97% rename from repos/effect/packages/effect/src/HashSet.ts rename to packages/effect/src/HashSet.ts index 2f088d062..df2ed97dc 100644 --- a/repos/effect/packages/effect/src/HashSet.ts +++ b/packages/effect/src/HashSet.ts @@ -7,6 +7,7 @@ * union, intersection, difference, subset checks, mapping, filtering, and * reducing helpers. * + * @stability stable * @since 2.0.0 */ @@ -45,6 +46,7 @@ const TypeId = internal.HashSetTypeId * smaller // => HashSet.make("apple", "cherry") * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -74,6 +76,7 @@ export interface HashSet extends Iterable, Equal, Pipeable, In * processFruit("apple") // => "Processing apple" * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace HashSet { @@ -122,6 +125,7 @@ export declare namespace HashSet { * withValues // => HashSet.make("hello", "world") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -142,6 +146,7 @@ export const empty: () => HashSet = internal.empty * HashSet.make("hello", 42, true) // => HashSet.make("hello", 42, true) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -164,6 +169,7 @@ export const make: >( * HashSet.fromIterable("hello") // => HashSet.make("h", "e", "l", "o") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -185,6 +191,7 @@ export const fromIterable: (values: Iterable) => HashSet = internal.fro * HashSet.isHashSet(null) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -212,6 +219,7 @@ export const isHashSet: { * HashSet.add(set, "a") // => HashSet.make("a", "b") * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -254,6 +262,7 @@ export const add: { * HashSet.has(people, new Person("Alice")) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -284,6 +293,7 @@ export const has: { * HashSet.remove(set, "d") // => HashSet.make("a", "b", "c") * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -310,6 +320,7 @@ export const remove: { * HashSet.size(HashSet.fromIterable(["x", "y", "z", "x", "y"])) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -328,6 +339,7 @@ export const size: (self: HashSet) => number = internal.size * HashSet.isEmpty(HashSet.make("a")) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -344,6 +356,7 @@ export const isEmpty: (self: HashSet) => boolean = internal.isEmpty * HashSet.union(HashSet.make("a", "b"), HashSet.make("b", "c")) // => HashSet.make("a", "b", "c") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -366,6 +379,7 @@ export const union: { * HashSet.intersection(HashSet.make("a", "b", "c"), HashSet.make("b", "c", "d")) // => HashSet.make("b", "c") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -388,6 +402,7 @@ export const intersection: { * HashSet.difference(HashSet.make("a", "b", "c"), HashSet.make("b", "d")) // => HashSet.make("a", "c") * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -417,6 +432,7 @@ export const difference: { * HashSet.isSubset(small, small) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -447,6 +463,7 @@ export const isSubset: { * lengths // => HashSet.make(5, 6) * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -469,6 +486,7 @@ export const map: { * HashSet.filter(HashSet.make(1, 2, 3, 4, 5, 6), (n) => n % 2 === 0) // => HashSet.make(2, 4, 6) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -504,6 +522,7 @@ export const filter: { * HashSet.some(HashSet.empty(), (n) => n > 0) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -531,6 +550,7 @@ export const some: { * HashSet.every(HashSet.empty(), (n) => n > 0) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -554,6 +574,7 @@ export const every: { * HashSet.reduce(numbers, 0, (acc, n) => acc + n) // => 15 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Inspectable.ts b/packages/effect/src/Inspectable.ts similarity index 98% rename from repos/effect/packages/effect/src/Inspectable.ts rename to packages/effect/src/Inspectable.ts index 47843fffa..ac0ecdad6 100644 --- a/repos/effect/packages/effect/src/Inspectable.ts +++ b/packages/effect/src/Inspectable.ts @@ -7,6 +7,7 @@ * the `Inspectable` interface, safe conversion helpers, and shared prototype or * class implementations for custom values. * + * @stability stable * @since 2.0.0 */ import { format, formatJson } from "./Formatter.ts" @@ -43,6 +44,7 @@ import { redact } from "./Redactable.ts" * obj[Inspectable.NodeInspectSymbol]() // => "CustomObject(hello)" * ``` * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -74,6 +76,7 @@ export const NodeInspectSymbol = Symbol.for("nodejs.util.inspect.custom") * obj[Inspectable.NodeInspectSymbol]() // => "CustomObject(test)" * ``` * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -120,6 +123,7 @@ export type NodeInspectSymbol = typeof NodeInspectSymbol * success.toString() // => "{\"_tag\":\"Success\",\"value\":42}" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -147,6 +151,7 @@ export interface Inspectable { * * @see {@link toStringUnknown} for converting unknown values to strings * + * @stability stable * @category converting * @since 4.0.0 */ @@ -181,6 +186,7 @@ export const toJson = (input: unknown): unknown => { * provided whitespace setting when possible, and values that cannot be * formatted are converted with `String`. * + * @stability stable * @category converting * @since 2.0.0 */ @@ -228,6 +234,7 @@ export const toStringUnknown = (u: unknown, whitespace: number | string | undefi * MyClass.prototype.constructor = MyClass * ``` * + * @stability stable * @category prototypes * @since 2.0.0 */ @@ -285,6 +292,7 @@ export const BaseProto: Inspectable = { * user[Inspectable.NodeInspectSymbol]() // => { _tag: "User", id: 1, name: "Alice", email: "alice@example.com" } * ``` * + * @stability stable * @category models * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Iterable.ts b/packages/effect/src/Iterable.ts similarity index 98% rename from repos/effect/packages/effect/src/Iterable.ts rename to packages/effect/src/Iterable.ts index be5547629..380637585 100644 --- a/repos/effect/packages/effect/src/Iterable.ts +++ b/packages/effect/src/Iterable.ts @@ -6,6 +6,7 @@ * fold iterable values while preserving the input as an iterable instead of * forcing an array first. * + * @stability stable * @since 2.0.0 */ @@ -51,6 +52,7 @@ import type { NoInfer } from "./Types.ts" * Array.from(first10) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -90,6 +92,7 @@ export const makeBy = (f: (i: number) => A, options?: { * Array.from(Iterable.range(1, 3)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -118,6 +121,7 @@ export const range = (start: number, end?: number): Iterable => { * Array.from(Iterable.replicate("a", 3)) // => ["a", "a", "a"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -141,6 +145,7 @@ export const replicate: { * * @see {@link forever} for repeating without an upper bound * @see {@link replicate} for repeating a single value + * @stability stable * @category constructors * @since 4.0.0 */ @@ -165,6 +170,7 @@ export const repeat: { * @see {@link repeat} for repeating an iterable a specific number of times * @see {@link take} for bounding the unbounded result before materializing it * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -182,6 +188,7 @@ export const forever = (self: Iterable): Iterable => repeat(self, Infin * Array.from(Iterable.fromRecord(x)) // => [["a", 1], ["b", 2], ["c", 3]] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -213,6 +220,7 @@ export const fromRecord = (self: Readonly>): I * Array.from(withZ) // => ["z", "a", "b", "c"] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -232,6 +240,7 @@ export const prepend: { * Array.from(Iterable.prependAll([1, 2], ["a", "b"])) // => ["a", "b", 1, 2] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -273,6 +282,7 @@ export const prependAll: { * @see {@link prepend} for adding one element before the existing elements * @see {@link appendAll} for appending all elements from another iterable * + * @stability stable * @category combining * @since 2.0.0 */ @@ -320,6 +330,7 @@ export const append: { * @see {@link append} for appending one value instead of another iterable * @see {@link prependAll} for yielding another iterable before `self` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -375,6 +386,7 @@ export const appendAll: { * Array.from(runningMax) // => [-Infinity, 3, 3, 4, 4, 5, 9, 9] * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -413,6 +425,7 @@ export const scan: { * Iterable.isEmpty([1, 2, 3]) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -444,6 +457,7 @@ export const isEmpty = (self: Iterable): self is Iterable => { * Iterable.size(range) // => 100 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -481,6 +495,7 @@ export const size = (self: Iterable): number => { * doubled // => Option.some(10) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -521,6 +536,7 @@ export const head = (self: Iterable): Option => { * Iterable.headUnsafe(nonEmpty) // => 1 * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -562,6 +578,7 @@ export const headUnsafe = (self: Iterable): A => { * Array.from(firstFive) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -619,6 +636,7 @@ export const take: { * Array.from(stringPrefix) // => ["a", "b", "c"] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -673,6 +691,7 @@ export const takeWhile: { * Array.from(slice) // => [2, 3, 4] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -738,6 +757,7 @@ export const drop: { * findSquareRoot // => Option.some(1) * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -798,6 +818,7 @@ export const findFirst: { * lastString // => Option.some("world") * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -862,6 +883,7 @@ export const findLast: { * Array.from(indexed) // => [[0, "apple"], [1, "banana"], [2, "cherry"]] * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -917,6 +939,7 @@ export const zip: { * Array.from(totals) // => [21.98, 25.5, 15] * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -982,6 +1005,7 @@ export const zipWith: { * Array.from(css).join("") // => "color: red; font-size: 14px; margin: 10px" * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1044,6 +1068,7 @@ export const intersperse: { * hasAlmostTwo // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1093,6 +1118,7 @@ export const containsWith = (isEquivalent: (self: A, that: A) => boolean): { * containsThree([4, 5, 6]) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1136,6 +1162,7 @@ export const contains: { * Array.from(Iterable.take(batchSums, 3)) // => [55, 155, 255] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -1202,6 +1229,7 @@ export const chunksOf: { * Array.from(scatteredGroups) // => [[1], [2], [1], [2], [1]] * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -1273,6 +1301,7 @@ export const groupWith: { * // Note: Only consecutive equal objects are grouped together * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -1327,6 +1356,7 @@ export const group: (self: Iterable) => Iterable> = group * evenOdd // => { odd: [1, 3, 5], even: [2, 4, 6] } * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -1381,6 +1411,7 @@ const constEmptyIterator: Iterator = { * Array.from(Iterable.empty()) // => [] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1418,6 +1449,7 @@ export const empty = (): Iterable => constEmpty * Array.from(evensOnly) // => [2, 4] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1453,6 +1485,7 @@ export const of = (a: A): Iterable => [a] * )) // => [3, 5, 7] * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1512,6 +1545,7 @@ export const map: { * Array.from(indexed) // => ["a", "b", "b", "c", "c", "c"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1555,6 +1589,7 @@ export const flatMap: { * Array.from(flatWithEmpty) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1627,6 +1662,7 @@ export const flatten = (self: Iterable>): Iterable => ({ * Array.from(evenIndexItems) // => ["0: a", "2: c", "4: e"] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1689,6 +1725,7 @@ export const filterMap: { * Array.from(indexedUntilC) // => ["0: a", "1: b"] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1726,6 +1763,7 @@ export const filterMapWhile: { * Array.from(Iterable.getSomes([Option.some(1), Option.none(), Option.some(2)])) // => [1, 2] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1765,6 +1803,7 @@ export const getSomes = (self: Iterable>): Iterable => { * ])) // => ["err"] * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1804,6 +1843,7 @@ export const getFailures = (self: Iterable>): Iterable = * ])) // => [1, 2] * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1867,6 +1907,7 @@ export const getSuccesses = (self: Iterable>): Iterable * Array.from(processed) // => [30, 40, 50] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1939,6 +1980,7 @@ export const filter: { * Array.from(foundValues) // => [1, 2, 3] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -1992,6 +2034,7 @@ export const flatMapNullishOr: { * hasString // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2048,6 +2091,7 @@ export const some: { * Array.from(collatz) // => [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2106,6 +2150,7 @@ export const unfold = (b: B, f: (b: B) => Option): Iterab * processed // => [[1, 2], [3, 4], [5, 6]] * ``` * + * @stability stable * @category traversing * @since 2.0.0 */ @@ -2166,6 +2211,7 @@ export const forEach: { * indexed // => ["0: a", "1: b", "2: c"] * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -2223,6 +2269,7 @@ export const reduce: { * Array.from(dedupedFloats) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -2290,6 +2337,7 @@ export const dedupeAdjacentWith: { * Array.from(cleanedData) // => [100, 101, 102, 100] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -2336,6 +2384,7 @@ export const dedupeAdjacent: (self: Iterable) => Iterable = dedupeAdjac * Array.from(testCases) // => ["admin_can_read", "admin_can_write", "admin_can_delete", "user_can_read", "user_can_write", "user_can_delete"] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -2410,6 +2459,7 @@ export const cartesianWith: { * Array.from(withEmpty) // => [] * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -2432,6 +2482,7 @@ export const cartesian: { * Iterable.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0) // => 2 * ``` * + * @stability stable * @category folding * @since 3.16.0 */ diff --git a/repos/effect/packages/effect/src/JsonPatch.ts b/packages/effect/src/JsonPatch.ts similarity index 99% rename from repos/effect/packages/effect/src/JsonPatch.ts rename to packages/effect/src/JsonPatch.ts index 8d0943dd3..929001ade 100644 --- a/repos/effect/packages/effect/src/JsonPatch.ts +++ b/packages/effect/src/JsonPatch.ts @@ -5,6 +5,7 @@ * difference between two JSON documents, serialize that difference, and replay * it without mutating the original input. * + * @stability stable * @since 4.0.0 */ import * as InternalRecord from "./internal/record.ts" @@ -56,6 +57,7 @@ import type * as Schema from "./Schema.ts" * @see {@link JsonPatch} for the array of operations forming a complete patch * @see {@link get} to compute operations automatically from value differences * @see {@link apply} to apply operations to transform documents + * @stability stable * @category models * @since 4.0.0 */ @@ -131,6 +133,7 @@ export type JsonPatchOperation = * @see {@link JsonPatchOperation} for individual operation types * @see {@link get} to generate patches from value differences * @see {@link apply} to execute patches to transform documents + * @stability stable * @category models * @since 4.0.0 */ @@ -174,6 +177,7 @@ export type JsonPatch = ReadonlyArray * * @see {@link apply} to apply the generated patch to a document * @see {@link JsonPatchOperation} for the operation types in the patch + * @stability stable * @category transforming * @since 4.0.0 */ @@ -276,6 +280,7 @@ function getLoop( * * @see {@link get} to generate patches from value differences * @see {@link JsonPatchOperation} for the operation types being applied + * @stability stable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/JsonPointer.ts b/packages/effect/src/JsonPointer.ts similarity index 98% rename from repos/effect/packages/effect/src/JsonPointer.ts rename to packages/effect/src/JsonPointer.ts index 78392b9da..b0aab9d94 100644 --- a/repos/effect/packages/effect/src/JsonPointer.ts +++ b/packages/effect/src/JsonPointer.ts @@ -4,6 +4,7 @@ * document, so token text must encode literal `~` and `/` characters. URI * fragments additionally apply percent-encoding after JSON Pointer escaping. * + * @stability stable * @since 4.0.0 */ @@ -36,6 +37,7 @@ * ``` * * @see {@link unescapeToken} The inverse operation for decoding escaped tokens + * @stability stable * @category encoding * @since 4.0.0 */ @@ -72,6 +74,7 @@ export function escapeToken(token: string): string { * ``` * * @see {@link escapeToken} The inverse operation for encoding tokens + * @stability stable * @category decoding * @since 4.0.0 */ @@ -129,6 +132,7 @@ export function decodeUriFragment(fragment: string): string | undefined { * ``` * * @see {@link formatUriFragment} for the inverse operation + * @stability stable * @category decoding * @since 4.0.0 */ @@ -162,6 +166,7 @@ export function parseUriFragment(fragment: string): ReadonlyArray | unde * ``` * * @see {@link parseUriFragment} for the inverse operation + * @stability stable * @category encoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/JsonSchema.ts b/packages/effect/src/JsonSchema.ts similarity index 99% rename from repos/effect/packages/effect/src/JsonSchema.ts rename to packages/effect/src/JsonSchema.ts index 5cbd69837..125b00f61 100644 --- a/repos/effect/packages/effect/src/JsonSchema.ts +++ b/packages/effect/src/JsonSchema.ts @@ -6,6 +6,7 @@ * JSON Schema Draft-04. The module also defines document types, meta-schema * constants, and OpenAPI component-key helpers. * + * @stability stable * @since 4.0.0 */ import * as InternalRecord from "./internal/record.ts" @@ -24,6 +25,7 @@ import * as Predicate from "./Predicate.ts" * This is an open record type (`[x: string]: unknown`) so it can hold any JSON * Schema keyword. Most functions in this module accept or return this type. * + * @stability stable * @category models * @since 4.0.0 */ @@ -49,6 +51,7 @@ export interface JsonSchema { * @see {@link Document} for a single root schema tagged with a dialect * @see {@link MultiDocument} for multiple root schemas tagged with a dialect * + * @stability stable * @category models * @since 4.0.0 */ @@ -61,6 +64,7 @@ export type Dialect = "draft-04" | "draft-07" | "draft-2020-12" | "openapi-3.1" * * Use to restrict a JSON Schema `type` keyword to the supported primitive names. * + * @stability stable * @category models * @since 4.0.0 */ @@ -81,6 +85,7 @@ export type Type = "string" | "number" | "boolean" | "array" | "object" | "null" * * @see {@link Document} for a single root schema with definitions * @see {@link MultiDocument} for multiple root schemas sharing definitions + * @stability stable * @category models * @since 4.0.0 */ @@ -123,6 +128,7 @@ export interface Definitions extends Record {} * * @see {@link MultiDocument} * @see {@link fromSchemaDraft2020_12} + * @stability stable * @category models * @since 4.0.0 */ @@ -147,6 +153,7 @@ export interface Document { * * @see {@link Document} * @see {@link toMultiDocumentOpenApi3_1} + * @stability stable * @category models * @since 4.0.0 */ @@ -165,6 +172,7 @@ export interface MultiDocument { * value for the root `$schema` field. * * @see {@link META_SCHEMA_URI_DRAFT_07} for the Draft-07 `$schema` URI + * @stability stable * @category constants * @since 4.0.0 */ @@ -186,6 +194,7 @@ export const META_SCHEMA_URI_DRAFT_04 = "http://json-schema.org/draft-04/schema# * @see {@link META_SCHEMA_URI_DRAFT_04} for the Draft-04 `$schema` URI * @see {@link META_SCHEMA_URI_DRAFT_2020_12} for the Draft 2020-12 `$schema` URI * + * @stability stable * @category constants * @since 4.0.0 */ @@ -206,6 +215,7 @@ export const META_SCHEMA_URI_DRAFT_07 = "http://json-schema.org/draft-07/schema# * * @see {@link META_SCHEMA_URI_DRAFT_07} for the Draft-07 `$schema` URI * + * @stability stable * @category constants * @since 4.0.0 */ @@ -276,6 +286,7 @@ const OPEN_API_31_TARGET_COLLISIONS = ["example", "discriminator", "xml", "exter * @see {@link fromSchemaDraft2020_12} * @see {@link fromSchemaOpenApi3_0} * @see {@link toDocumentDraft07} + * @stability stable * @category decoding * @since 4.0.0 */ @@ -313,6 +324,7 @@ export function fromSchemaDraft07(js: JsonSchema): Document<"draft-2020-12"> { * * @see {@link fromSchemaDraft07} * @see {@link fromSchemaOpenApi3_1} + * @stability stable * @category decoding * @since 4.0.0 */ @@ -365,6 +377,7 @@ export function fromSchemaDraft2020_12(js: JsonSchema): Document<"draft-2020-12" * * @see {@link fromSchemaOpenApi3_0} * @see {@link toMultiDocumentOpenApi3_1} + * @stability stable * @category decoding * @since 4.0.0 */ @@ -426,6 +439,7 @@ export function fromSchemaOpenApi3_1(js: JsonSchema): Document<"draft-2020-12"> * * @see {@link fromSchemaOpenApi3_1} * @see {@link fromSchemaDraft07} + * @stability stable * @category decoding * @since 4.0.0 */ @@ -480,6 +494,7 @@ export function fromSchemaOpenApi3_0(schema: JsonSchema): Document<"draft-2020-1 * @see {@link fromSchemaDraft07} * @see {@link toDocumentDraft04} for converting to Draft-04 * @see {@link toMultiDocumentOpenApi3_1} + * @stability stable * @category encoding * @since 4.0.0 */ @@ -532,6 +547,7 @@ export function toDocumentDraft07(document: Document<"draft-2020-12">): Document * ``` * * @see {@link toDocumentDraft07} for converting to Draft-07 + * @stability stable * @category encoding * @since 4.0.0 */ @@ -592,6 +608,7 @@ export function toDocumentDraft04(document: Document<"draft-2020-12">): Document * * @see {@link toDocumentDraft07} * @see {@link MultiDocument} + * @stability stable * @category encoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Latch.ts b/packages/effect/src/Latch.ts similarity index 97% rename from repos/effect/packages/effect/src/Latch.ts rename to packages/effect/src/Latch.ts index 62e73d736..59c2c4787 100644 --- a/repos/effect/packages/effect/src/Latch.ts +++ b/packages/effect/src/Latch.ts @@ -5,6 +5,7 @@ * includes effectful and synchronous constructors plus helpers to open, release, * close, wait, and gate effects behind the latch. * + * @stability stable * @since 4.0.0 */ import type * as Effect from "./Effect.ts" @@ -46,6 +47,7 @@ import * as internal from "./internal/effect.ts" * @see {@link open} for releasing current and future waiters * @see {@link release} for releasing only the current waiters * + * @stability stable * @category models * @since 4.0.0 */ @@ -155,6 +157,7 @@ export interface Latch { * * @see {@link make} for creating a latch inside Effect code * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -190,6 +193,7 @@ export const makeUnsafe: (open?: boolean | undefined) => Latch = internal.makeLa * * @see {@link makeUnsafe} for synchronous allocation outside Effect code * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -210,6 +214,7 @@ export const make: (open?: boolean | undefined) => Effect.Effect = intern * @see {@link openUnsafe} for a synchronous variant * @see {@link release} to release waiting fibers without opening the latch * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -233,6 +238,7 @@ export const open = (self: Latch): Effect.Effect => self.open * @see {@link release} to release waiting fibers without opening the latch * @see {@link closeUnsafe} for the synchronous inverse operation * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -254,6 +260,7 @@ export const openUnsafe = (self: Latch): boolean => self.openUnsafe() * * @see {@link open} for opening the latch for current and future waiters * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -284,6 +291,7 @@ export { * @see {@link release} for resuming current waiters without opening the latch * @see {@link whenOpen} for waiting before running another effect * + * @stability stable * @category getters * @since 4.0.0 */ @@ -306,6 +314,7 @@ export { * @see {@link closeUnsafe} for a synchronous variant * @see {@link open} for opening the latch for current and future waiters * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -330,6 +339,7 @@ export const close = (self: Latch): Effect.Effect => self.close * @see {@link openUnsafe} to synchronously open the latch and release waiting * fibers * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -354,6 +364,7 @@ export const closeUnsafe = (self: Latch): boolean => self.closeUnsafe() * @see {@link open} for opening the latch for current and future waiters * @see {@link release} for resuming current waiters without opening the latch * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -376,6 +387,7 @@ export const whenOpen: { * * Use to check the state of the latch without suspending or changing its state. * + * @stability stable * @category predicates * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Layer.ts b/packages/effect/src/Layer.ts similarity index 98% rename from repos/effect/packages/effect/src/Layer.ts rename to packages/effect/src/Layer.ts index aed2f1067..982f864e8 100644 --- a/repos/effect/packages/effect/src/Layer.ts +++ b/packages/effect/src/Layer.ts @@ -7,6 +7,7 @@ * combine with other layers, provide services to effects or streams, and attach * error handling, tracing, or lifecycle hooks. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts" @@ -48,6 +49,7 @@ const TypeId = "~effect/Layer" * provides, `E` as the possible errors during layer construction, and `RIn` as * the services this layer requires as dependencies. * + * @stability stable * @category models * @since 2.0.0 */ @@ -68,6 +70,7 @@ export interface Layer extends Variance * This is used by Effect's pipe and unification machinery to preserve the * provided services, error, and requirements of a `Layer`. * + * @stability stable * @category models * @since 4.0.0 */ @@ -84,6 +87,7 @@ export interface LayerUnify { * Type-level marker used by `Unify` for `Layer` types that should be ignored * during unification. * + * @stability stable * @category models * @since 4.0.0 */ @@ -92,6 +96,7 @@ export interface LayerUnifyIgnore {} /** * The variance interface for Layer type parameters. * + * @stability stable * @category models * @since 2.0.0 */ @@ -121,6 +126,7 @@ export interface Variance { * @see {@link Error} for extracting construction errors from a layer type * @see {@link Success} for extracting provided services from a layer type * + * @stability stable * @category utility types * @since 3.9.0 */ @@ -142,6 +148,7 @@ export interface Any { * @see {@link Success} for extracting the services provided by the same `Layer` * @see {@link Error} for extracting the construction failure type from the same `Layer` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -159,6 +166,7 @@ export type Services = T extends infer L * @see {@link Success} for extracting the services provided by the same `Layer` * @see {@link Services} for extracting the dependency requirements of the same `Layer` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -174,6 +182,7 @@ export type Error = T extends Layer "result" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -285,6 +295,7 @@ const memoMapObserve = (entry: MemoMapEntry, scope: Scope.Scope): boolean => { * Layer.isLayer(notALayer) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -342,6 +353,7 @@ const fromBuildUnsafe = ( * Effect.runSync(Effect.provide(program, databaseLayer)) // => "result" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -389,6 +401,7 @@ export const fromBuild = ( * Effect.runSync(Effect.provide(program, databaseLayer)) // => "result" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -484,6 +497,7 @@ class MemoMapImpl implements MemoMap { * Effect.runSync(database.query("SELECT 1")) // => "result" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -503,6 +517,7 @@ export const makeMemoMapUnsafe = (): MemoMap => new MemoMapImpl() * @see {@link forkMemoMap} for allocating the child memo map inside `Effect` * @see {@link makeMemoMapUnsafe} for creating a root memo map without a parent * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -537,6 +552,7 @@ export const forkMemoMapUnsafe = (parent: MemoMap): MemoMap => new MemoMapImpl(p * Effect.runSync(database.query("SELECT 1")) // => "result" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -556,6 +572,7 @@ export const makeMemoMap: Effect = internalEffect.sync(makeMemoMapUnsaf * @see {@link forkMemoMapUnsafe} for the synchronous constructor variant * @see {@link buildWithMemoMap} for building layers with an explicit memo map * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -576,6 +593,7 @@ export const forkMemoMap = (parent: MemoMap): Effect => internalEffect. * * @see {@link MemoMap} the memoization map type wrapped by this service * + * @stability stable * @category services * @since 3.13.0 */ @@ -637,6 +655,7 @@ export class CurrentMemoMap extends Context.Service()(" * logs // => ["ready"] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -656,7 +675,7 @@ export const buildWithMemoMap: { scope: Scope.Scope ): Effect, E, RIn> => internalEffect.provideService( - internalEffect.map(self.build(memoMap, scope), Context.add(CurrentMemoMap, memoMap)), + internalEffect.map(internalEffect.suspend(() => self.build(memoMap, scope)), Context.add(CurrentMemoMap, memoMap)), CurrentMemoMap, memoMap )) @@ -691,6 +710,7 @@ export const buildWithMemoMap: { * Effect.runSync(Effect.scoped(program)) // => "result" * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -754,6 +774,7 @@ export const build = ( * logs // => ["Initializing database...", "Database closed"] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -799,6 +820,7 @@ export const buildWithScope: { * * @see {@link sync} for constructing layers from lazy values * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -856,6 +878,7 @@ export const succeed: { * * @see {@link succeed} for providing a single service from a value * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -882,6 +905,7 @@ export const succeedContext = (context: Context.Context): Layer => * * @see {@link effectDiscard} for running an effect while providing no services * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -918,6 +942,7 @@ export const empty: Layer = succeedContext(Context.empty()) * * @see {@link succeed} for constructing layers from static values * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -965,6 +990,7 @@ export const sync: { * @see {@link sync} for lazily providing a single service * @see {@link succeedContext} for providing an already available context * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1006,6 +1032,7 @@ export const syncContext = (evaluate: LazyArg>): Layer * @see {@link effectContext} for effectfully providing multiple services * @see {@link effectDiscard} for running construction work without providing services * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1065,6 +1092,7 @@ const effectImpl = ( * * @see {@link effect} for effectfully providing a single service * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1098,6 +1126,7 @@ export const effectContext = ( * * @see {@link empty} for a no-op layer that performs no construction work * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1129,6 +1158,7 @@ export const effectDiscard = (effect: Effect): Layer "https://api.example.com" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1168,6 +1198,7 @@ const unwrapKey = Context.Service>("effect/Layer/unwrap") * Effect.runSync(Effect.provide(program, unwrappedLayer)) // => "result" * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1237,6 +1268,7 @@ const mergeAllEffect = , ...Array, ...Array ["[DEBUG] Starting database query"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1692,6 +1728,7 @@ export const flatMap: { * @see {@link tapError} for running an effect when layer construction fails with a typed error * @see {@link tapCause} for running an effect when layer construction fails with any cause * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1731,6 +1768,7 @@ export const tap: { * @see {@link tap} for running an effect when layer construction succeeds * @see {@link tapCause} for inspecting the full failure cause, including defects and interruption * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1774,6 +1812,7 @@ export const tapError: { * @see {@link tapError} for observing only typed layer construction errors * @see {@link catchCause} for recovering from a layer construction failure by switching to another layer * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -1842,6 +1881,7 @@ export const tapCause: { * Effect.runSync(Effect.exit(program)) // => Exit.die(error) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -1879,6 +1919,7 @@ export { * @see {@link catchTag} for recovering from specific tagged errors * @see {@link catchCause} for recovering with access to the full cause * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -1916,6 +1957,7 @@ export { * * @see {@link catchCause} for recovering with access to the full cause * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2022,6 +2064,7 @@ export const catchTag: { * * @see {@link catchTag} for recovering from specific tagged errors * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2059,6 +2102,7 @@ export const catchCause: { * transformation function `f`, and replaces the old service with the * transformed one. * + * @stability stable * @category providing services * @since 3.13.0 */ @@ -2156,6 +2200,7 @@ export const updateService: { * await Effect.runPromise(program) // => { shared: true, fresh: false } * ``` * + * @stability stable * @category layers * @since 2.0.0 */ @@ -2203,6 +2248,7 @@ export const fresh = (self: Layer): Layer => * await Effect.runPromise(program) // => ["Starting HTTP server..."] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -2226,6 +2272,7 @@ export const launch = (self: Layer): Effect "Test User" * ``` * + * @stability stable * @category testing * @since 3.17.0 */ @@ -2383,6 +2431,7 @@ const ChannelTypeId: Channel.TypeId = "~effect/Channel" * const validLayer = satisfiesNumber(numberLayer) * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -2411,6 +2460,7 @@ export const satisfiesSuccessType = * const validLayer = satisfiesError(typeErrorLayer) * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -2440,6 +2490,7 @@ export const satisfiesErrorType = * const validLayer = satisfiesNumber(numberLayer) * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -2468,6 +2519,7 @@ export const satisfiesServicesType = * @see {@link span} for creating a layer span * @see {@link withSpan} for wrapping layer construction in a span * + * @stability stable * @category options * @since 4.0.0 */ @@ -2528,6 +2580,7 @@ export interface SpanOptions extends Tracer.SpanOptions { * logs // => ["Connecting to database", "Database connected", "database-init", "Span database-init ended with: Success"] * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -2586,6 +2639,7 @@ export const span = ( * Effect.runSync(Effect.provide(program, databaseLayer)) // => { spanId: "42", result: "Result: SELECT 1" } * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -2649,6 +2703,7 @@ export const parentSpan = (span: Tracer.AnySpan): Layer => * logs // => ["Application ready", "Application initialization completed: Success"] * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -2759,6 +2814,7 @@ export const withSpan: { * Effect.runSync(Effect.scoped(program)) // => { dbResult: "DB: SELECT * FROM users", cacheResult: "Cache: user:123" } * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/LayerMap.ts b/packages/effect/src/LayerMap.ts similarity index 99% rename from repos/effect/packages/effect/src/LayerMap.ts rename to packages/effect/src/LayerMap.ts index 014766e37..4091a4559 100644 --- a/repos/effect/packages/effect/src/LayerMap.ts +++ b/packages/effect/src/LayerMap.ts @@ -7,6 +7,7 @@ * keyed resource families such as tenant clients, regional connections, or * environment-specific services. * + * @stability unstable * @since 3.14.0 */ import * as Context from "./Context.ts" @@ -72,6 +73,7 @@ type IdleTimeToLiveInput = Duration.Input | ((key: K) => Duration.Input) * await Effect.runPromise(Effect.scoped(program)) // => { development: "development: SELECT 1", production: "production: SELECT 1" } * ``` * + * @stability unstable * @category models * @since 3.14.0 */ @@ -150,6 +152,7 @@ export interface LayerMap { * await Effect.runPromise(Effect.scoped(program)) // => "development: SELECT * FROM users" * ``` * + * @stability unstable * @category constructors * @since 3.14.0 */ @@ -256,6 +259,7 @@ export const make: < * await Effect.runPromise(Effect.scoped(program)) // => { development: "DEV: SELECT 1", production: "PROD: SELECT 1" } * ``` * + * @stability unstable * @category constructors * @since 3.14.0 */ @@ -302,6 +306,7 @@ export const fromRecord = < * * @see {@link Service} for creating concrete `LayerMap` service classes * + * @stability unstable * @category services * @since 3.14.0 */ @@ -402,6 +407,7 @@ export interface TagClass< * await Effect.runPromise(program) // => "Hello, John!" * ``` * + * @stability unstable * @category services * @since 3.14.0 */ @@ -488,6 +494,7 @@ export const Service = () => /** * Type helpers for values created with `LayerMap.Service`. * + * @stability unstable * @since 3.14.0 */ export declare namespace Service { diff --git a/repos/effect/packages/effect/src/LayerRef.ts b/packages/effect/src/LayerRef.ts similarity index 98% rename from repos/effect/packages/effect/src/LayerRef.ts rename to packages/effect/src/LayerRef.ts index 4d18aeb85..f850c5e72 100644 --- a/repos/effect/packages/effect/src/LayerRef.ts +++ b/packages/effect/src/LayerRef.ts @@ -5,6 +5,7 @@ * back as a layer or scoped effect, and supports invalidation so later users can * acquire a fresh context. * + * @stability unstable * @since 4.0.0 */ import * as Context from "./Context.ts" @@ -37,6 +38,7 @@ const TypeId = "~effect/LayerRef" * @see {@link make} for constructing a `LayerRef` from a layer * @see {@link Service} for defining a `LayerRef` as a service class * + * @stability unstable * @category models * @since 4.0.0 */ @@ -127,6 +129,7 @@ export interface LayerRef { * * @see {@link Service} for defining a reusable service class around a `LayerRef` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -208,6 +211,7 @@ export const make = Effect.fnUntraced( * * @see {@link Service} for creating concrete `LayerRef` service classes * + * @stability unstable * @category services * @since 4.0.0 */ @@ -301,6 +305,7 @@ export interface TagClass< * * @see {@link make} for creating a `LayerRef` value without defining a service class * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/LogLevel.ts b/packages/effect/src/LogLevel.ts similarity index 97% rename from repos/effect/packages/effect/src/LogLevel.ts rename to packages/effect/src/LogLevel.ts index 837e1bd5a..2868cd0c2 100644 --- a/repos/effect/packages/effect/src/LogLevel.ts +++ b/packages/effect/src/LogLevel.ts @@ -4,6 +4,7 @@ * equality and ordering instances, threshold comparison helpers, and an effect * for checking whether a level is enabled by the current logging settings. * + * @stability stable * @since 2.0.0 */ import type * as Effect from "./Effect.ts" @@ -61,6 +62,7 @@ import * as References from "./References.ts" * levels // => ["Error", "Debug"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -81,6 +83,7 @@ export type LogLevel = "All" | "Fatal" | "Error" | "Warn" | "Info" | "Debug" | " * @see {@link values} for the runtime list of all accepted `LogLevel` values, * including sentinels * + * @stability stable * @category models * @since 4.0.0 */ @@ -108,6 +111,7 @@ export type Severity = "Fatal" | "Error" | "Warn" | "Info" | "Debug" | "Trace" * @see {@link Severity} for the concrete message severity type that excludes `All` and `None` * @see {@link Order} for comparing these levels by severity order * + * @stability stable * @category constants * @since 4.0.0 */ @@ -135,6 +139,7 @@ export const values: ReadonlyArray = ["All", "Fatal", "Error", "Warn", * LogLevel.Order("Info", "Info") // => 0 * ``` * + * @stability stable * @category ordering * @since 2.0.0 */ @@ -164,6 +169,7 @@ export const Order: Ord.Order = effect.LogLevelOrder * @see {@link Order} for severity ordering rather than exact level equality * @see {@link isGreaterThanOrEqualTo} for minimum-threshold checks * + * @stability stable * @category instances * @since 4.0.0 */ @@ -192,6 +198,7 @@ export const Equivalence: Equ.Equivalence = Equ.strictEqual( * @see {@link Order} for comparing log levels without exposing numeric keys * @see {@link isGreaterThanOrEqualTo} for minimum-threshold filtering * + * @stability stable * @category ordering * @since 4.0.0 */ @@ -230,6 +237,7 @@ export const getOrdinal = (self: LogLevel): number => effect.logLevelToOrder(sel * isMoreSevereThanInfo("Debug") // => false * ``` * + * @stability stable * @category ordering * @since 4.0.0 */ @@ -263,6 +271,7 @@ export const isGreaterThan: { * isInfoOrAbove("Error") // => true * ``` * + * @stability stable * @category ordering * @since 4.0.0 */ @@ -304,6 +313,7 @@ export const isGreaterThanOrEqualTo: { * isLessSevereThanError("Fatal") // => false * ``` * + * @stability stable * @category ordering * @since 4.0.0 */ @@ -337,6 +347,7 @@ export const isLessThan: { * isInfoOrBelow("Debug") // => true * ``` * + * @stability stable * @category ordering * @since 4.0.0 */ @@ -377,6 +388,7 @@ export const isLessThanOrEqualTo: { * await Effect.runPromise(warnOnly) // => { debugEnabled: false, errorEnabled: true } * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Logger.ts b/packages/effect/src/Logger.ts similarity index 97% rename from repos/effect/packages/effect/src/Logger.ts rename to packages/effect/src/Logger.ts index 2cd9b881a..16edc09af 100644 --- a/repos/effect/packages/effect/src/Logger.ts +++ b/packages/effect/src/Logger.ts @@ -7,6 +7,7 @@ * logger references, console routing helpers, built-in formatters, batching, * file logging, and layers for installing loggers. * + * @stability stable * @since 2.0.0 */ import * as Array from "./Array.ts" @@ -58,6 +59,7 @@ const TypeId = "~effect/Logger" * messages // => ["[Info] Hello World"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -95,6 +97,7 @@ export interface Logger extends Pipeable { * outputs // => [{ message: ["Processing request"], level: "Info", hasCause: false }] * ``` * + * @stability stable * @category options * @since 2.0.0 */ @@ -121,6 +124,7 @@ export interface Options { * Logger.isLogger({ log: () => {} }) // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -156,6 +160,7 @@ export const isLogger = (u: unknown): u is Logger => Predicate * messages // => [["Hello from custom logger"]] * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -179,6 +184,7 @@ export const CurrentLoggers: Context.Reference> * @see {@link consolePretty} for the TTY-mode pretty console logger affected by this reference * @see {@link withConsoleError} for routing a specific formatter logger to `console.error` * + * @stability stable * @category services * @since 4.0.0 */ @@ -214,6 +220,7 @@ export const LogToStderr: Context.Reference = effect.LogToStderr * outputs // => [{ message: "HELLO" }] * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -259,6 +266,7 @@ export const map = dual< * await Effect.runPromise(program) // => ["Info: Hello World"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -299,6 +307,7 @@ export const withConsoleLog = ( * await Effect.runPromise(program) // => ["ERROR: Database connection failed"] * ``` * + * @stability stable * @category logging * @since 2.0.0 */ @@ -353,6 +362,7 @@ export const withConsoleError = ( * messages // => expected * ``` * + * @stability stable * @category logging * @since 3.8.0 */ @@ -465,6 +475,7 @@ const format = ( * outputs // => ["Info: Hello World"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -483,6 +494,7 @@ export const make: ( * Logger.isLogger(Logger.defaultLogger) // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -518,6 +530,7 @@ export const defaultLogger: Logger = effect.defaultLogger * await Effect.runPromise(program) // => ["level=INFO message=\"Application started\""] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -553,6 +566,7 @@ export const formatSimple = effect.loggerMake(format(escapeDoubleQuotes)) * await Effect.runPromise(program) // => ["level=INFO message=\"User login\""] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -588,6 +602,7 @@ export const formatLogFmt = effect.loggerMake(format(JSON.stringify, 0)) * await Effect.runPromise(program) // => [{ message: "User action", level: "INFO" }] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -658,6 +673,7 @@ export const formatStructured: Logger ["{\"message\":\"Server started\",\"level\":\"INFO\"}"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -696,6 +712,7 @@ export const formatJson = map(formatStructured, Formatter.formatJson) * flushed // => [["Event 1", "Event 2"]] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -720,16 +737,16 @@ export const batched = dual< readonly flush: (messages: Array>) => Effect.Effect } ): Effect.Effect, never, Scope.Scope> => - effect.flatMap(effect.scope, (scope) => { + effect.suspend(() => { let buffer: Array = [] - const flush = effect.suspend(() => { + const flush = effect.uninterruptible(effect.suspend(() => { if (buffer.length === 0) { return effect.void } const arr = buffer buffer = [] return options.flush(arr) - }) + })) return effect.uninterruptibleMask((restore) => restore( @@ -739,8 +756,12 @@ export const batched = dual< ) ).pipe( effect.forkDetach, - effect.flatMap((fiber) => effect.scopeAddFinalizerExit(scope, () => effect.fiberInterrupt(fiber))), - effect.andThen(effect.addFinalizer(() => flush)), + effect.flatMap((fiber) => + effect.addFinalizer(() => + // Wait for any in-flight flush before draining the remaining buffer. + effect.andThen(effect.fiberInterrupt(fiber), flush) + ) + ), effect.as( effect.loggerMake((options) => { buffer.push(self.log(options)) @@ -795,6 +816,7 @@ export const batched = dual< * * @see {@link consolePrettyBrowser} for browser-specific implementation * @see {@link consolePrettyTty} for the TTY-mode implementation + * @stability stable * @category constructors * @since 4.0.0 */ @@ -832,6 +854,7 @@ export const consolePretty: ( * ``` * * @see {@link consolePretty} for the platform-independent implementation + * @stability stable * @category constructors * @since 4.0.0 */ @@ -884,6 +907,7 @@ export const consolePrettyBrowser: ( * ``` * * @see {@link consolePretty} for the platform-independent implementation + * @stability stable * @category constructors * @since 4.0.0 */ @@ -911,6 +935,7 @@ export const consolePrettyTty: ( * Logger.isLogger(Logger.consoleLogFmt) // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -936,6 +961,7 @@ export const consoleLogFmt: Logger = withConsoleLog(formatLogFmt) * Logger.isLogger(Logger.consoleStructured) // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -959,6 +985,7 @@ export const consoleStructured: Logger = withConsoleLog(formatStr * Logger.isLogger(Logger.consoleJson) // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -989,6 +1016,7 @@ export const consoleJson: Logger = withConsoleLog(formatJson) * Effect.runSync(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1021,6 +1049,7 @@ export const tracerLogger: Logger = effect.tracerLogger * messages // => [["Application started"]] * ``` * + * @stability stable * @category layers * @since 4.0.0 */ @@ -1109,6 +1138,7 @@ export const layer = < * writes // => ["Application started"] * ``` * + * @stability unstable * @category logging * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ManagedRuntime.ts b/packages/effect/src/ManagedRuntime.ts similarity index 95% rename from repos/effect/packages/effect/src/ManagedRuntime.ts rename to packages/effect/src/ManagedRuntime.ts index 877935c68..9f11d4412 100644 --- a/repos/effect/packages/effect/src/ManagedRuntime.ts +++ b/packages/effect/src/ManagedRuntime.ts @@ -7,12 +7,14 @@ * and runners for connecting Effect programs to JavaScript entry points such as * promises, callbacks, and synchronous code. * + * @stability stable * @since 2.0.0 */ import type * as Context from "./Context.ts" import * as Effect from "./Effect.ts" import * as Exit from "./Exit.ts" import * as Fiber from "./Fiber.ts" +import { type FiberImpl, scopeRemoveFinalizerUnsafe } from "./internal/effect.ts" import * as Layer from "./Layer.ts" import { hasProperty } from "./Predicate.ts" import * as Scope from "./Scope.ts" @@ -39,6 +41,7 @@ const TypeId = "~effect/ManagedRuntime" * * @see {@link make} for creating managed runtimes this guard recognizes * + * @stability stable * @category guards * @since 3.9.0 */ @@ -53,6 +56,7 @@ export const isManagedRuntime = (input: unknown): input is ManagedRuntime { * @see {@link Layer.MemoMap} for shared layer memoization * @see {@link Layer.build} for lower-level scoped layer construction * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -340,7 +346,12 @@ export const make = ( [Symbol.asyncDispose](): Promise { return self.dispose() }, - disposeEffect: Effect.suspend(() => { + disposeEffect: Effect.withFiber((fiber) => { + // Closing fiberScope interrupts and awaits every managed fiber. Skip the + // disposing fiber and its ancestors, which would otherwise await themselves. + for (let current = fiber as FiberImpl | undefined; current; current = current._parent) { + scopeRemoveFinalizerUnsafe(fiberScope, current) + } ;(self as Mutable>).contextEffect = Effect.die("ManagedRuntime disposed") self.cachedContext = undefined return Scope.close(self.scope, Exit.void) diff --git a/repos/effect/packages/effect/src/Match.ts b/packages/effect/src/Match.ts similarity index 98% rename from repos/effect/packages/effect/src/Match.ts rename to packages/effect/src/Match.ts index 1f3a4aaad..4cd164619 100644 --- a/repos/effect/packages/effect/src/Match.ts +++ b/packages/effect/src/Match.ts @@ -8,6 +8,7 @@ * patterns, and common checks such as strings, numbers, records, and class * instances. * + * @stability stable * @since 4.0.0 */ import * as internal from "./internal/matcher.ts" @@ -39,6 +40,7 @@ type ValueTagHandlers = { /** * Marker used by `Matcher` to distinguish matchers created with `Match.value`. * + * @stability stable * @category models * @since 4.0.0 */ @@ -77,6 +79,7 @@ export type ValueFlavor = "value" * result // => "string: some input" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -118,6 +121,7 @@ export type Matcher< * matcher(42) // => "Number: 42" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -170,6 +174,7 @@ export interface TypeMatcher< * result // => "User: Alice" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -212,6 +217,7 @@ export interface ValueMatcher< * @see {@link When} for positive cases * @see {@link Not} for negative cases * + * @stability stable * @category models * @since 4.0.0 */ @@ -242,6 +248,7 @@ export type Case = When | Not * stringMatcher(42) // => "Got number: 42" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -276,6 +283,7 @@ export interface When { * matcher("forbidden") // => "This string is forbidden" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -324,6 +332,7 @@ export interface Not { * * @see {@link value} for creating a matcher from a specific value. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -351,6 +360,7 @@ export const type: () => Matcher, I, never, never> = * format("status", "a") // => "status: A" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -398,6 +408,7 @@ export const fn: , I>( * * @see {@link type} for creating a matcher from a specific type. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -431,6 +442,7 @@ export const value: ( * message // => "Success: Hello" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -494,6 +506,7 @@ export const valueTags: { * processResult({ _tag: "Loading" }) // => { type: "pending" } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -547,6 +560,7 @@ export const typeTags: { * ) * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -600,6 +614,7 @@ export const withReturnType: () => "Invalid field: email" * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -720,6 +736,7 @@ export const whenOr: < * checkUser({ age: 20, role: "user" }) // => "Access denied" * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -781,6 +798,7 @@ export const whenAnd: < * @see {@link discriminators} for defining several discriminator handlers at once * @see {@link discriminatorStartsWith} for matching string discriminator values by prefix * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -832,6 +850,7 @@ export const discriminator: ( * * @see {@link discriminator} for matching exact discriminator values * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -895,6 +914,7 @@ export const discriminatorStartsWith: ( * @see {@link discriminator} for adding one discriminator case to a matcher pipeline * @see {@link discriminatorsExhaustive} for handling every discriminator value and finalizing the matcher * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -958,6 +978,7 @@ export const discriminators: ( * * @see {@link discriminators} for defining discriminator handlers without finalizing the matcher * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1015,6 +1036,7 @@ export const discriminatorsExhaustive: ( * match({ _tag: "error", error: new Error("Oops!") }) // => "Error: Oops!" * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1065,6 +1087,7 @@ export const tag: < * match({ _tag: "A.A" }) // => 1 * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1120,6 +1143,7 @@ export const tagStartsWith: < * match({ _tag: "A", a: "ok" }) // => "ok" * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1175,6 +1199,7 @@ export const tags: < * match({ _tag: "B", b: 42 }) // => 42 * ``` * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1224,6 +1249,7 @@ export const tagsExhaustive: < * * @see {@link when} for adding a positive pattern case * + * @stability stable * @category defining patterns * @since 4.0.0 */ @@ -1280,6 +1306,7 @@ export const not: < * * @see {@link string} for matching any string * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1321,6 +1348,7 @@ export const nonEmptyString: SafeRefinement = internal.nonEmptySt * handleStatus("pending") // => "Unknown status: pending" * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1353,6 +1381,7 @@ export const is: < * processValue(true) // => "Boolean: yes" * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1394,6 +1423,7 @@ export const string: Predicate.Refinement = Predicate.isString * * @see {@link bigint} for matching primitive bigint values * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1443,6 +1473,7 @@ export const number: Predicate.Refinement = Predicate.isNumber * @see {@link defined} for matching only non-nullish values * @see {@link orElse} for providing a fallback after earlier cases * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1486,6 +1517,7 @@ export const any: SafeRefinement = internal.any * * @see {@link any} for matching every value without excluding nullish inputs * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1527,6 +1559,7 @@ export const defined: (u: A) => u is A & {} = internal.defined * * @see {@link is} for matching specific literal boolean values * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1549,6 +1582,7 @@ export { * @see {@link defined} for matching non-nullish values * @see {@link is} for matching literal values * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1572,6 +1606,7 @@ export { * @see {@link defined} for matching non-nullish values * @see {@link is} for matching literal values * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1614,6 +1649,7 @@ export { * * @see {@link number} for matching primitive number values * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1652,6 +1688,7 @@ export const bigint: Predicate.Refinement = Predicate.isBigInt * handleSymbol("string") // => "Not a symbol" * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1694,6 +1731,7 @@ export const symbol: Predicate.Refinement = Predicate.isSymbol * * @see {@link instanceOf} for matching instances of any constructor * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1739,6 +1777,7 @@ export const date: Predicate.Refinement = Predicate.isDate * * @see {@link instanceOf} for matching a specific constructor * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1798,6 +1837,7 @@ export const record: Predicate.Refinement any>( * * @see {@link instanceOf} for type-safe constructor matching * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1886,6 +1927,7 @@ export const instanceOfUnsafe: any>( * @see {@link result} for returning unmatched input as a `Result` failure * @see {@link orElseAbsurd} for finalizing when unmatched input should be impossible * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1938,6 +1980,7 @@ export const orElse: , F extends (_: RA, ...arg * @see {@link exhaustive} for compile-time exhaustive matcher finalization * @see {@link orElse} for providing a fallback for unmatched input * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1980,6 +2023,7 @@ export const orElseAbsurd: >( * getRole({ role: "viewer" })._tag // => "Failure" * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -2029,6 +2073,7 @@ export const result: >( * @see {@link result} for preserving unmatched input as a `Result` failure * @see {@link orElse} for replacing unmatched input with a fallback value * + * @stability stable * @category completion * @since 4.0.0 */ @@ -2067,6 +2112,7 @@ export const option: >( * ) * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -2105,6 +2151,7 @@ const SafeRefinementId = "~effect/Match/SafeRefinement" * processValue(null) // => "Undefined or null" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -2125,6 +2172,7 @@ type Fail = typeof Fail * application. These types enable the sophisticated type inference that makes * pattern matching both type-safe and ergonomic. * + * @stability stable * @since 4.0.0 */ export declare namespace Types { diff --git a/repos/effect/packages/effect/src/Metric.ts b/packages/effect/src/Metric.ts similarity index 99% rename from repos/effect/packages/effect/src/Metric.ts rename to packages/effect/src/Metric.ts index c4a2cb31a..9b8e31635 100644 --- a/repos/effect/packages/effect/src/Metric.ts +++ b/packages/effect/src/Metric.ts @@ -8,6 +8,7 @@ * histogram boundaries, registry snapshots, text dumps, and controls for * enabling runtime metrics. * + * @stability stable * @since 2.0.0 */ @@ -105,6 +106,7 @@ import type { Contravariant, Covariant } from "./Types.ts" * ] // => [1, 128, 1] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -183,6 +185,7 @@ export interface Metric extends Pipeable { * const counts = [result.requests.count, result.bytes.count] // => [6, 1024n] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -243,6 +246,7 @@ export interface Counter extends Metric [3, 3, 1024000n] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -317,6 +321,7 @@ export interface CounterState { * const values = [result.statusAnalysis.mostFrequent, result.actionAnalysis.mostFrequent] // => ["200", "login"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -401,6 +406,7 @@ export interface Frequency extends Metric {} * mostCommon.map(({ key, count }) => [key, count]) // => [["200", 3], ["click", 3]] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -462,6 +468,7 @@ export interface FrequencyState { * const values = [result.memory.currentValue, result.disk.currentValue] // => [704, 5000000000n] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -530,6 +537,7 @@ export interface Gauge extends Metric [23.1, 5000000000n, 15] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -618,6 +626,7 @@ export interface GaugeState { * values // => [4, 445, 118] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -705,6 +714,7 @@ export interface Histogram extends Metric {} * values // => [5, 50, 750, 1295] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -810,6 +820,7 @@ export interface HistogramState { * counts // => [5, 1461, 3] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -892,6 +903,7 @@ export interface Summary extends Metric {} * values // => [7, 45, 890, 1879] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -945,6 +957,7 @@ export interface SummaryState { * const values = [result.counter.count, result.gauge.value] // => [1, 12] * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace Metric { @@ -1555,6 +1568,7 @@ export declare namespace Metric { * const key = result // => { keyValue: "effect/Metric/CurrentMetricAttributes", keyType: "string", isConstant: true } * ``` * + * @stability stable * @category constants * @since 4.0.0 */ @@ -1602,6 +1616,7 @@ export const CurrentMetricAttributesKey = "effect/Metric/CurrentMetricAttributes * const actual = attributes // => { service: "api", version: "1.0" } * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -1614,6 +1629,7 @@ const MetricRegistryKey = "effect/Metric/MetricRegistry" /** * The registry used to store metric metadata and hooks. * + * @stability stable * @category services * @since 4.0.0 */ @@ -1642,6 +1658,7 @@ export type MetricRegistry = Map> * @see {@link snapshot} for reading all registered metrics from the current `Effect` context * @see {@link snapshotUnsafe} for reading all registered metrics from an explicit `Context` * + * @stability stable * @category services * @since 4.0.0 */ @@ -2017,6 +2034,7 @@ class MetricTransform extends Metric$ false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -2077,6 +2095,7 @@ export const isMetric = (u: unknown): u is Metric => * const counts = [result.requestValue.count, result.eventValue.count, result.bytesValue.count] // => [6, 1, 1024n] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2163,6 +2182,7 @@ export const counter: { * const values = [result.memoryValue.value, result.cpuValue.value, result.diskValue.value] // => [800, 75, 1024000000n] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2257,6 +2277,7 @@ export const gauge: { * counts // => [3, 2, 2] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2336,6 +2357,7 @@ export const frequency = (name: string, options?: { * values // => [5, 500, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2412,6 +2434,7 @@ export const histogram = (name: string, options: { * const payloadValues = [payload.count, payload.min, payload.max, payload.sum] // => [4, 1.2, 15.6, 26] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2464,6 +2487,7 @@ export const summary = (name: string, options: { * const metadata = [responseTimesSummary.id, responseTimesSummary.type] // => ["response_times_summary", "Summary"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2517,6 +2541,7 @@ export const summaryWithTimestamp = (name: string, options: { * ) // => { count: 1, min: 120, max: 120, sum: 120 } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2577,6 +2602,7 @@ export const timer = (name: string, options?: { * ) // => { requestCount: 1, count: 1, min: 750, max: 750, average: 750 } * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -2627,6 +2653,7 @@ export const value = ( * await Effect.runPromise(Effect.provideService(program, Metric.MetricRegistry, new Map())) // => [22, 15] * ``` * + * @stability stable * @category mutations * @since 3.6.5 */ @@ -2689,6 +2716,7 @@ export const modify: { * await Effect.runPromise(Effect.provideService(program, Metric.MetricRegistry, new Map())) // => [67.8, 2, 3] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -2734,6 +2762,7 @@ export const update: { * const values = [value.count, value.sum] // => [1, 250] * ``` * + * @stability stable * @category annotations * @since 2.0.0 */ @@ -2796,6 +2825,7 @@ export const mapInput: { * const count = value.count // => 3 * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2861,6 +2891,7 @@ export const withConstantInput: { * await Effect.runPromise(Effect.provideService(result, Metric.MetricRegistry, new Map())) // => [2, 1] * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -2926,6 +2957,7 @@ export const withAttributes: { * const ids = snapshots.map((snapshot) => snapshot.id).sort() // => ["http_requests", "response_time_ms"] * ``` * + * @stability stable * @category snapshotting * @since 2.0.0 */ @@ -2985,6 +3017,7 @@ export const snapshot: Effect> = InternalEffect.m * included // => [true, true, true] * ``` * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -3053,6 +3086,7 @@ export const dump: Effect = InternalEffect.flatMap(InternalEffect.contex * await Effect.runPromise(Effect.provideService(program, Metric.MetricRegistry, new Map())) // => ["http_requests"] * ``` * + * @stability stable * @category snapshotting * @since 4.0.0 */ @@ -3142,6 +3176,7 @@ const attributesToString = (attributes: Metric.AttributeSet): string => { * Metric.boundariesFromIterable([-5, 0, 10, 10, 25, 50]) // => [10, 25, 50, Infinity] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3167,6 +3202,7 @@ export const boundariesFromIterable = (iterable: Iterable): ReadonlyArra * Metric.linearBoundaries({ start: 10, width: 20, count: 5 }) // => [10, 30, 50, 70, Infinity] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3193,6 +3229,7 @@ export const linearBoundaries = (options: { * Metric.exponentialBoundaries({ start: 1, factor: 2, count: 5 }) // => [1, 2, 4, 8, Infinity] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3232,6 +3269,7 @@ const fiberFailures = counter("child_fiber_failures", { * Metric.FiberRuntimeMetricsKey // => "effect/Metric/FiberRuntimeMetrics" * ``` * + * @stability stable * @category constants * @since 4.0.0 */ @@ -3260,6 +3298,7 @@ export const FiberRuntimeMetricsKey: "effect/Metric/FiberRuntimeMetrics" = Inter * events // => ["start", "success"] * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -3299,6 +3338,7 @@ export interface FiberRuntimeMetricsService { * const isDefault = result // => true * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -3321,6 +3361,7 @@ export const FiberRuntimeMetrics = Context.Reference ["function", "function"] * ``` * + * @stability stable * @category services * @since 4.0.0 */ @@ -3361,6 +3402,7 @@ export const FiberRuntimeMetricsImpl: FiberRuntimeMetricsService = { * await Effect.runPromise(Effect.provide(program, Metric.enableRuntimeMetricsLayer)) // => true * ``` * + * @stability stable * @category layers * @since 4.0.0 */ @@ -3400,6 +3442,7 @@ export const enableRuntimeMetricsLayer = Layer.succeed(FiberRuntimeMetrics)(Fibe * const values = [result.counterValue.count, result.metricsEnabled] // => [1, false] * ``` * + * @stability stable * @category layers * @since 4.0.0 */ @@ -3427,6 +3470,7 @@ export const disableRuntimeMetricsLayer = Layer.succeed(FiberRuntimeMetrics)(und * await Effect.runPromise(Metric.enableRuntimeMetrics(program)) // => true * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -3456,6 +3500,7 @@ export const enableRuntimeMetrics: (self: Effect) => Effect true * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/MutableHashMap.ts b/packages/effect/src/MutableHashMap.ts similarity index 98% rename from repos/effect/packages/effect/src/MutableHashMap.ts rename to packages/effect/src/MutableHashMap.ts index 2d788f18b..6ec7e0167 100644 --- a/repos/effect/packages/effect/src/MutableHashMap.ts +++ b/packages/effect/src/MutableHashMap.ts @@ -7,6 +7,7 @@ * Effect `Equal` and `Hash`, so callers can mix reference-based and structural * lookup in the same collection. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyArray } from "./Array.ts" @@ -57,6 +58,7 @@ const TypeId = "~effect/MutableHashMap" * @see {@link get} for reading values by key * @see {@link set} for mutating entries by key * + * @stability stable * @category models * @since 2.0.0 */ @@ -83,6 +85,7 @@ export interface MutableHashMap extends Iterable<[K, V]>, Pipeable * * @see {@link MutableHashMap} for the mutable hash map interface * + * @stability stable * @category guards * @since 4.0.0 */ @@ -138,6 +141,7 @@ const MutableHashMapProto: Omit, "backing" | "b * @see {@link make} for creating a map from explicit entries * @see {@link fromIterable} for creating a map from an iterable of entries * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -173,6 +177,7 @@ export const empty = (): MutableHashMap => { * @see {@link empty} for creating an empty map * @see {@link fromIterable} for creating a map from an iterable of entries * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -214,6 +219,7 @@ export const make: >( * @see {@link make} for creating a map from explicit entries * @see {@link empty} for creating an empty map * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -254,6 +260,7 @@ export const fromIterable = (entries: Iterable): MutableH * @see {@link has} for checking only whether a key is present * @see {@link set} for inserting or replacing a value by key * + * @stability stable * @category getters * @since 2.0.0 */ @@ -303,6 +310,7 @@ const isSimpleKey = (u: unknown): boolean => typeof u !== "object" && typeof u ! * @see {@link values} for iterating over stored values * @see {@link has} for checking one key without iterating * + * @stability stable * @category getters * @since 3.8.0 */ @@ -337,6 +345,7 @@ export const keys = (self: MutableHashMap): Iterable => self.back * * @see {@link keys} for iterating over stored keys * + * @stability stable * @category getters * @since 3.8.0 */ @@ -380,6 +389,7 @@ const getFromBucket = ( * * @see {@link get} for reading the value as an `Option` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -427,6 +437,7 @@ export const has: { * @see {@link modifyAt} for setting or removing based on the current optional value * @see {@link remove} for deleting an entry by key * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -505,6 +516,7 @@ const getRefKey = ( * @see {@link set} for inserting or replacing a value directly * @see {@link modifyAt} for handling both missing and existing keys * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -585,6 +597,7 @@ export const modify: { * @see {@link set} for inserting or replacing directly * @see {@link remove} for deleting directly * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -652,6 +665,7 @@ export const modifyAt: { * @see {@link clear} for removing all entries * @see {@link modifyAt} for conditionally removing based on the current value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -721,6 +735,7 @@ export const remove: { * @see {@link remove} for deleting one key * @see {@link empty} for creating a fresh empty map * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -758,6 +773,7 @@ export const clear = (self: MutableHashMap) => { * * @see {@link isEmpty} for checking whether the map has no entries * + * @stability stable * @category getters * @since 2.0.0 */ @@ -772,6 +788,7 @@ export const size = (self: MutableHashMap): number => self.backing.s * * @see {@link size} for reading the exact number of entries * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -793,6 +810,7 @@ export const isEmpty = (self: MutableHashMap): boolean => self.backi * @see {@link keys} for iterating only keys * @see {@link values} for iterating only values * + * @stability stable * @category traversing * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/MutableHashSet.ts b/packages/effect/src/MutableHashSet.ts similarity index 98% rename from repos/effect/packages/effect/src/MutableHashSet.ts rename to packages/effect/src/MutableHashSet.ts index 654f7c633..9b5f3787b 100644 --- a/repos/effect/packages/effect/src/MutableHashSet.ts +++ b/packages/effect/src/MutableHashSet.ts @@ -7,6 +7,7 @@ * follows the same hashing and equality rules as the underlying mutable hash * map. * + * @stability stable * @since 2.0.0 */ import { format } from "./Formatter.ts" @@ -59,6 +60,7 @@ const TypeId = "~effect/MutableHashSet" * MutableHashSet.size(set) // => 3 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -84,6 +86,7 @@ export interface MutableHashSet extends Iterable, Pipeable, Inspectabl * * @see {@link MutableHashSet} for the mutable hash set interface * + * @stability stable * @category guards * @since 4.0.0 */ @@ -148,6 +151,7 @@ const fromHashMap = (keyMap: MutableHashMap.MutableHashMap): Muta * @see {@link fromIterable} for creating a set from an iterable of values * @see {@link clear} for emptying an existing mutable set * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -179,6 +183,7 @@ export const empty = (): MutableHashSet => fromHashMap(MutableHash * Array.from(MutableHashSet.fromIterable("hello")) // => ["h", "e", "l", "o"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -212,6 +217,7 @@ export const fromIterable = (keys: Iterable): MutableHashSet => * MutableHashSet.size(MutableHashSet.make("hello", 42, true, "hello")) // => 3 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -251,6 +257,7 @@ export const make = >( * MutableHashSet.size(set) // => 3 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -296,6 +303,7 @@ export const add: { * @see {@link add} for adding a value to the set * @see {@link remove} for removing a value from the set * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -339,6 +347,7 @@ export const has: { * MutableHashSet.size(set) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -377,6 +386,7 @@ export const remove: { * MutableHashSet.size(set) // => 0 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -411,6 +421,7 @@ export const size = (self: MutableHashSet): number => MutableHashMap.size( * MutableHashSet.size(set) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/MutableList.ts b/packages/effect/src/MutableList.ts similarity index 97% rename from repos/effect/packages/effect/src/MutableList.ts rename to packages/effect/src/MutableList.ts index 061e9d6a8..634d7e8c7 100644 --- a/repos/effect/packages/effect/src/MutableList.ts +++ b/packages/effect/src/MutableList.ts @@ -6,6 +6,7 @@ * the same list object in place and keep its `length` field current. Taking * from an empty list returns the `Empty` symbol. * + * @stability stable * @since 4.0.0 */ import * as Arr from "./Array.ts" @@ -30,6 +31,7 @@ import * as Count from "./internal/count.ts" * list.length // => 0 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -43,6 +45,7 @@ export interface MutableList { * The MutableList namespace contains type definitions and utilities for working * with mutable linked lists. * + * @stability stable * @since 2.0.0 */ export declare namespace MutableList { @@ -103,6 +106,7 @@ export declare namespace MutableList { * MutableList.take(list) === MutableList.Empty // => true * ``` * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -129,6 +133,7 @@ export const Empty: unique symbol = Symbol.for("effect/MutableList/Empty") * takeAndDouble(list) // => 10 * ``` * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -150,6 +155,7 @@ export type Empty = typeof Empty * list.length // => 0 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -202,6 +208,7 @@ const emptyBucket = (): MutableList.Bucket => ({ * list.length // => 3 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -234,6 +241,7 @@ export const append = (self: MutableList, message: A): void => { * MutableList.toArray(list) // => ["first", "second", "third", "last"] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -266,6 +274,7 @@ export const prepend = (self: MutableList, message: A): void => { * MutableList.toArray(list) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -299,6 +308,7 @@ export const prependAll = (self: MutableList, messages: Iterable): void * MutableList.toArray(list) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -334,6 +344,7 @@ export const prependAllUnsafe = (self: MutableList, messages: ReadonlyArra * list.length // => 5 * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -368,6 +379,7 @@ export const appendAll = (self: MutableList, messages: Iterable): numbe * MutableList.toArray(list) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -409,6 +421,7 @@ export const appendAllUnsafe = (self: MutableList, messages: ReadonlyArray * MutableList.take(list) === MutableList.Empty // => true * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -440,6 +453,7 @@ export const clear = (self: MutableList): void => { * list.length // => 7 * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -493,6 +507,7 @@ export const takeN = (self: MutableList, n: number): Array => { * @see {@link takeN} for removing up to `n` values and returning them as an array * @see {@link clear} for removing every value from the list * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -538,6 +553,7 @@ export const takeNVoid = (self: MutableList, n: number): void => { * list.length // => 0 * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -561,6 +577,7 @@ export const takeAll = (self: MutableList): Array => takeN(self, self.l * list.length // => 2 * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -598,6 +615,7 @@ export const take = (self: MutableList): Empty | A => { * * @see {@link takeN} for removing up to `n` values and returning them as an array * + * @stability stable * @category converting * @since 4.0.0 */ @@ -629,6 +647,7 @@ export const toArrayN = (self: MutableList, n: number): Array => { * * @see {@link takeAll} for converting all elements to an array and clearing the list * + * @stability stable * @category converting * @since 4.0.0 */ @@ -652,6 +671,7 @@ export const toArray = (self: MutableList): Array => toArrayN(self, sel * MutableList.toArray(list) // => [2, 4, 6, 8, 10] * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -711,6 +731,7 @@ export const filter = (self: MutableList, f: (value: A, i: number) => bool * MutableList.toArray(list) // => ["banana", "cherry"] * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/MutableRef.ts b/packages/effect/src/MutableRef.ts similarity index 98% rename from repos/effect/packages/effect/src/MutableRef.ts rename to packages/effect/src/MutableRef.ts index acd270380..49be9ec68 100644 --- a/repos/effect/packages/effect/src/MutableRef.ts +++ b/packages/effect/src/MutableRef.ts @@ -7,6 +7,7 @@ * and updating the value, plus numeric increment/decrement helpers and a * boolean toggle helper. * + * @stability stable * @since 2.0.0 */ import * as Equal from "./Equal.ts" @@ -64,6 +65,7 @@ const TypeId = "~effect/MutableRef" * config.current // => { timeout: 10000, retries: 5 } * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -112,6 +114,7 @@ const MutableRefProto: Omit, "current"> = { * MutableRef.get(status) // => "running" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -166,6 +169,7 @@ export const make = (value: T): MutableRef => { * MutableRef.get(ref) // => "final" * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -218,6 +222,7 @@ export const compareAndSet: { * MutableRef.get(countdown) // => 0 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -261,6 +266,7 @@ export const decrement = (self: MutableRef): MutableRef => updat * MutableRef.get(attempts) // => -1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -299,6 +305,7 @@ export const decrementAndGet = (self: MutableRef): number => updateAndGe * value1 === value2 // => true * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -344,6 +351,7 @@ export const get = (self: MutableRef): T => self.current * nextIndex // => 2 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -396,6 +404,7 @@ export const getAndDecrement = (self: MutableRef): number => getAndUpdat * MutableRef.get(iterations) // => 5 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -445,6 +454,7 @@ export const getAndIncrement = (self: MutableRef): number => getAndUpdat * MutableRef.get(buffer) // => [] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -510,6 +520,7 @@ export const getAndSet: { * MutableRef.get(list) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -561,6 +572,7 @@ export const getAndUpdate: { * MutableRef.get(counter) // => 9 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -607,6 +619,7 @@ export const increment = (self: MutableRef): MutableRef => updat * MutableRef.get(attempts) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -659,6 +672,7 @@ export const incrementAndGet = (self: MutableRef): number => updateAndGe * MutableRef.get(state) // => "success" * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -716,6 +730,7 @@ export const set: { * MutableRef.get(ref1) // => 3 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -782,6 +797,7 @@ export const setAndGet: { * MutableRef.get(list) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -847,6 +863,7 @@ export const update: { * MutableRef.get(list) // => [2, 4, 6] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -907,6 +924,7 @@ export const updateAndGet: { * MutableRef.get(flag) // => true * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Newtype.ts b/packages/effect/src/Newtype.ts similarity index 97% rename from repos/effect/packages/effect/src/Newtype.ts rename to packages/effect/src/Newtype.ts index 4df67075f..f87bade4e 100644 --- a/repos/effect/packages/effect/src/Newtype.ts +++ b/packages/effect/src/Newtype.ts @@ -8,6 +8,7 @@ * helpers for reusing carrier instances such as `Equivalence`, `Order`, * `Combiner`, and `Reducer`. * + * @stability unstable * @since 4.0.0 */ import type * as Combiner from "./Combiner.ts" @@ -54,6 +55,7 @@ const TypeId = "~effect/Newtype" * @see {@link makeIso} — create an iso to wrap and unwrap * @see {@link value} — unwrap a newtype value * + * @stability unstable * @category models * @since 4.0.0 */ @@ -73,6 +75,7 @@ export interface Newtype { * Use to access generic constraints and type-level utilities for `Newtype` * values. * + * @stability unstable * @since 4.0.0 */ export declare namespace Newtype { @@ -144,6 +147,7 @@ export declare namespace Newtype { * * @see {@link makeIso} — two-way conversion (wrap and unwrap) * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -178,6 +182,7 @@ export const value: (newtype: N) => Newtype.Carrier = * * @see {@link value} — unwrap only * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -215,6 +220,7 @@ export function makeIso(): Optic.Iso( * * @see {@link makeEquivalence} — lift an `Equivalence` for the carrier * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -283,6 +290,7 @@ export const makeOrder: (order: Order.Order( * * @see {@link makeCombiner} — lift a `Combiner` for the carrier * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/NonEmptyIterable.ts b/packages/effect/src/NonEmptyIterable.ts similarity index 97% rename from repos/effect/packages/effect/src/NonEmptyIterable.ts rename to packages/effect/src/NonEmptyIterable.ts index b19aa8c61..8196cd22b 100644 --- a/repos/effect/packages/effect/src/NonEmptyIterable.ts +++ b/packages/effect/src/NonEmptyIterable.ts @@ -4,6 +4,7 @@ * `NonEmptyIterable` can be consumed anywhere an `Iterable` is expected, * while also carrying the guarantee that reading the first element is safe. * + * @stability stable * @since 2.0.0 */ @@ -23,6 +24,7 @@ * * @see {@link NonEmptyIterable} for the branded iterable type that uses this symbol * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -44,6 +46,7 @@ export declare const nonEmpty: unique symbol * The type is branded with a unique symbol to ensure type safety while maintaining * full compatibility with JavaScript's iteration protocol. * + * @stability stable * @category models * @since 2.0.0 */ @@ -76,6 +79,7 @@ export interface NonEmptyIterable extends Iterable { * globalThis.Array.from({ [Symbol.iterator]: () => rest }) // => [2, 3] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Number.ts b/packages/effect/src/Number.ts similarity index 96% rename from repos/effect/packages/effect/src/Number.ts rename to packages/effect/src/Number.ts index b7d8f3d61..03fdfa973 100644 --- a/repos/effect/packages/effect/src/Number.ts +++ b/packages/effect/src/Number.ts @@ -5,6 +5,7 @@ * checking, parsing, arithmetic, safe division, comparison, range checks, * clamping, rounding, ordering, equivalence, and numeric aggregation. * + * @stability stable * @since 2.0.0 */ import * as Equ from "./Equivalence.ts" @@ -39,6 +40,7 @@ import * as Reducer from "./Reducer.ts" * N.Number("3.14") // => 3.14 * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -60,6 +62,7 @@ export const Number = globalThis.Number * Number.isNumber("2") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -82,6 +85,7 @@ export const isNumber: (input: unknown) => input is number = predicate.isNumber * * @see {@link sumAll} for summing an iterable of numbers * + * @stability stable * @category math * @since 2.0.0 */ @@ -107,6 +111,7 @@ export const sum: { * * @see {@link multiplyAll} for multiplying an iterable of numbers * + * @stability stable * @category math * @since 2.0.0 */ @@ -130,6 +135,7 @@ export const multiply: { * Number.subtract(2, 3) // => -1 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -157,6 +163,7 @@ export const subtract: { * @see {@link divideUnsafe} for division that throws when the divisor is zero * @see {@link remainder} for the numeric remainder operation * + * @stability stable * @category math * @since 2.0.0 */ @@ -196,6 +203,7 @@ export const divide: { * * @see {@link divide} for division that returns `Option.none` when the divisor is zero * + * @stability stable * @category math * @since 4.0.0 */ @@ -223,6 +231,7 @@ export const divideUnsafe: { * Number.increment(2) // => 3 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -243,6 +252,7 @@ export const increment = (n: number): number => n + 1 * Number.decrement(3) // => 2 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -266,6 +276,7 @@ export const decrement = (n: number): number => n - 1 * Number.Order(1, 1) // => 0 * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -289,6 +300,7 @@ export const Order: order.Order = order.Number * Number.Equivalence(NaN, NaN) // => true * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -311,6 +323,7 @@ export const Equivalence: Equ.Equivalence = Equ.Number * Number.isLessThan(4, 3) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -336,6 +349,7 @@ export const isLessThan: { * Number.isLessThanOrEqualTo(4, 3) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -361,6 +375,7 @@ export const isLessThanOrEqualTo: { * Number.isGreaterThan(4, 3) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -386,6 +401,7 @@ export const isGreaterThan: { * Number.isGreaterThanOrEqualTo(4, 3) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -415,6 +431,7 @@ export const isGreaterThanOrEqualTo: { * * @see {@link clamp} for forcing a number into an inclusive range * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -458,6 +475,7 @@ export const between: { * * @see {@link between} for checking whether a number is already inside a range * + * @stability stable * @category math * @since 2.0.0 */ @@ -489,6 +507,7 @@ export const clamp: { * * @see {@link max} for selecting the larger value * + * @stability stable * @category math * @since 2.0.0 */ @@ -514,6 +533,7 @@ export const min: { * * @see {@link min} for selecting the smaller value * + * @stability stable * @category math * @since 2.0.0 */ @@ -539,6 +559,7 @@ export const max: { * Number.sign(5) // => 1 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -562,6 +583,7 @@ export const sign = (n: number): Ordering => Order(n, 0) * @see {@link sum} for adding two numbers * @see {@link ReducerSum} for summing through APIs that consume a `Reducer` * + * @stability stable * @category math * @since 2.0.0 */ @@ -591,6 +613,7 @@ export const sumAll = (collection: Iterable): number => { * @see {@link multiply} for multiplying two numbers * @see {@link ReducerMultiply} for multiplying through APIs that consume a `Reducer` * + * @stability stable * @category math * @since 2.0.0 */ @@ -625,6 +648,7 @@ export const multiplyAll = (collection: Iterable): number => { * * @see {@link divide} for quotient calculation with division-by-zero represented as `Option.none` * + * @stability stable * @category math * @since 2.0.0 */ @@ -679,6 +703,7 @@ function toScientificInteger(n: number): readonly [coefficient: bigint, exponent * Number.nextPow2(17) // => 32 * ``` * + * @stability stable * @category math * @since 2.0.0 */ @@ -710,6 +735,7 @@ export const nextPow2 = (n: number): number => { * * @see {@link Number} for native constructor coercion * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -746,6 +772,7 @@ export const parse = (s: string): Option.Option => { * Number.round(1.567, 2) // => 1.57 * ``` * + * @stability stable * @category math * @since 3.8.0 */ @@ -771,6 +798,7 @@ export const round: { * @see {@link sumAll} for summing an iterable directly * @see {@link ReducerMultiply} for multiplying number values * + * @stability stable * @category math * @since 4.0.0 */ @@ -794,6 +822,7 @@ export const ReducerSum: Reducer.Reducer = Reducer.make((a, b) => a + b, * * @see {@link multiplyAll} for multiplying an iterable directly * + * @stability stable * @category math * @since 4.0.0 */ @@ -825,6 +854,7 @@ export const ReducerMultiply: Reducer.Reducer = Reducer.make((a, b) => a * @see {@link ReducerMin} for keeping the smallest number * @see {@link max} for comparing two numbers directly * + * @stability stable * @category math * @since 4.0.0 */ @@ -849,6 +879,7 @@ export const ReducerMax: Reducer.Reducer = Reducer.make((a, b) => Math.m * @see {@link ReducerMax} for keeping the largest number * @see {@link min} for comparing two numbers directly * + * @stability stable * @category math * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Optic.ts b/packages/effect/src/Optic.ts similarity index 99% rename from repos/effect/packages/effect/src/Optic.ts rename to packages/effect/src/Optic.ts index c21149a6f..107432f29 100644 --- a/repos/effect/packages/effect/src/Optic.ts +++ b/packages/effect/src/Optic.ts @@ -9,6 +9,7 @@ * constructors, focusing helpers, and operations for replacing, modifying, or * collecting focused values. * + * @stability stable * @since 4.0.0 */ @@ -59,6 +60,7 @@ import type { IsUnion, NoInfer } from "./Types.ts" * @see {@link Lens} — when you only need a one-directional focus into a whole * @see {@link Prism} — when the focus may not be present * + * @stability stable * @category models * @since 4.0.0 */ @@ -95,6 +97,7 @@ export interface Iso extends Lens, Prism {} * @see {@link Iso} — the type this function returns * @see {@link id} — identity iso (no conversion) * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -134,6 +137,7 @@ export function makeIso(get: (s: S) => A, set: (a: A) => S): Iso { * @see {@link Iso} — when conversion is lossless in both directions * @see {@link Optional} — when reading can also fail * + * @stability stable * @category models * @since 4.0.0 */ @@ -172,6 +176,7 @@ export interface Lens extends Optional { * @see {@link Lens} — the type this function returns * @see {@link makeIso} — when no original `S` is needed for `set` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -218,6 +223,7 @@ export function makeLens(get: (s: S) => A, replace: (a: A, s: S) => S): Le * @see {@link fromChecks} — build a Prism from schema checks * @see {@link Lens} — when reading always succeeds * + * @stability stable * @category models * @since 4.0.0 */ @@ -261,6 +267,7 @@ export interface Prism extends Optional { * @see {@link Prism} — the type this function returns * @see {@link fromChecks} — build from `Schema` checks instead * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -304,6 +311,7 @@ export function makePrism( * @see {@link makePrism} — constructor with custom getter/setter * @see {@link Prism} — the type this function returns * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -440,6 +448,7 @@ type ForbidUnion = IsUnion extends true ? [Message * @see {@link Lens} — when reading always succeeds * @see {@link Prism} — when writing always succeeds * + * @stability stable * @category models * @since 4.0.0 */ @@ -915,6 +924,7 @@ export interface Optional { * @see {@link makeLens} — when reading always succeeds * @see {@link makePrism} — when writing always succeeds * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -961,6 +971,7 @@ export function makeOptional( * @see {@link getAll} — extract focused elements * @see {@link Optional} — the base type * + * @stability stable * @category models * @since 4.0.0 */ @@ -1291,6 +1302,7 @@ function composeKind(a: Kind, b: Kind): Kind { * * @see {@link getResult} for optics whose focus may be absent * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1315,6 +1327,7 @@ export const get: { * * @see {@link get} for optics that always focus * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1339,6 +1352,7 @@ export const getResult: { * * @see {@link replace} for updates that use an existing source * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1363,6 +1377,7 @@ export const set: { * * @see {@link replaceResult} for an explicit replacement failure * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1387,6 +1402,7 @@ export const replace: { * * @see {@link replace} for returning the original source on failure * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1426,6 +1442,7 @@ export const replaceResult: { * * @see {@link modifyAll} for transforming every value in a traversal * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1485,6 +1502,7 @@ export const modify: { * * @see {@link Traversal} — the optic type this operates on * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1515,6 +1533,7 @@ export const getAll: { * @see {@link modify} for transforming the focus as a whole * @see {@link getAll} for reading every focused value * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1573,6 +1592,7 @@ const identityIso = make([]) * * @see {@link Iso} — the type this function returns * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1611,6 +1631,7 @@ export function id(): Iso { * @see {@link Iso} — the type this function returns * @see {@link id} — identity iso * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1648,6 +1669,7 @@ export function entries(): Iso, ReadonlyArray(): Prism, A> { * @see {@link some} — focuses on `Some` instead * @see {@link Prism} — the type this function returns * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1726,6 +1749,7 @@ export function none(): Prism, undefined> { * @see {@link failure} — focuses on the failure side * @see {@link Prism} — the type this function returns * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1765,6 +1789,7 @@ export function success(): Prism, A> { * @see {@link success} — focuses on the success side * @see {@link Prism} — the type this function returns * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Option.ts b/packages/effect/src/Option.ts similarity index 98% rename from repos/effect/packages/effect/src/Option.ts rename to packages/effect/src/Option.ts index 6feed438b..23895f878 100644 --- a/repos/effect/packages/effect/src/Option.ts +++ b/packages/effect/src/Option.ts @@ -8,6 +8,7 @@ * and from common nullable or result-like shapes. It also includes `Option.gen` * for writing small generator-based computations that stop at the first `None`. * + * @stability stable * @since 2.0.0 */ import * as Combiner from "./Combiner.ts" @@ -49,6 +50,7 @@ const TypeId = "~effect/Option" * @see {@link none} for creating a `None` * @see {@link match} for pattern matching * + * @stability stable * @category models * @since 2.0.0 */ @@ -69,6 +71,7 @@ export type Option = None | Some * @see {@link isNone} to check if an `Option` is `None` * @see {@link none} to construct a `None` * + * @stability stable * @category models * @since 2.0.0 */ @@ -96,6 +99,7 @@ export interface None extends Pipeable, Inspectable { * * @see {@link gen} for writing generator-based `Option` code that consumes this iterator protocol * + * @stability stable * @category generators * @since 4.0.0 */ @@ -122,6 +126,7 @@ export interface OptionIterator> { * @see {@link isSome} to check if an `Option` is `Some` * @see {@link some} to construct a `Some` * + * @stability stable * @category models * @since 2.0.0 */ @@ -152,6 +157,7 @@ export interface Some extends Pipeable, Inspectable { * type when generic code returns or combines `Option` values. Users normally * do not need to reference this interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -166,6 +172,7 @@ export interface OptionUnify { * * Use to access type-level helpers associated with `Option`. * + * @stability stable * @since 2.0.0 */ export declare namespace Option { @@ -205,6 +212,7 @@ export declare namespace Option { * This supports type-level unification behavior for `Option`. Users normally * do not need to reference this interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -218,6 +226,7 @@ export interface OptionUnifyIgnore {} * Use when defining higher-kinded abstractions that must accept optional-value * types as one of their type-lambda inputs. * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -250,6 +259,7 @@ export interface OptionTypeLambda extends TypeLambda { * * @see {@link some} for the opposite operation. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -280,6 +290,7 @@ export const none = (): Option => option.none * * @see {@link none} for the opposite operation. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -311,6 +322,7 @@ export const some: (value: A) => Option = option.some * @see {@link isNone} to check for `None` specifically * @see {@link isSome} to check for `Some` specifically * + * @stability stable * @category guards * @since 2.0.0 */ @@ -338,6 +350,7 @@ export const isOption: (input: unknown) => input is Option = option.isO * * @see {@link isSome} for the opposite check. * + * @stability stable * @category guards * @since 2.0.0 */ @@ -365,6 +378,7 @@ export const isNone: (self: Option) => self is None = option.isNone * * @see {@link isNone} for the opposite check. * + * @stability stable * @category guards * @since 2.0.0 */ @@ -397,6 +411,7 @@ export const isSome: (self: Option) => self is Some = option.isSome * * @see {@link getOrElse} for unwrapping with a default * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -451,6 +466,7 @@ export const match: { * * @see {@link liftPredicate} for the reverse direction * + * @stability stable * @category converting * @since 2.0.0 */ @@ -481,6 +497,7 @@ export const toRefinement = (f: (a: A) => Option): (a: A) => * * @see {@link toArray} for the inverse direction * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -515,6 +532,7 @@ export const fromIterable = (collection: Iterable): Option => { * * @see {@link getFailure} for the opposite operation. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -544,6 +562,7 @@ export const getSuccess: (self: Result) => Option = result.getSuc * * @see {@link getSuccess} for the opposite operation. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -576,6 +595,7 @@ export const getFailure: (self: Result) => Option = result.getFai * @see {@link getOrUndefined} to fall back to `undefined` * @see {@link getOrThrow} to throw on `None` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -613,6 +633,7 @@ export const getOrElse: { * @see {@link orElseSome} to wrap the fallback value in `Some` automatically * @see {@link firstSomeOf} to pick the first `Some` from a collection * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -648,6 +669,7 @@ export const orElse: { * * @see {@link orElse} when the fallback is itself an `Option` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -686,6 +708,7 @@ export const orElseSome: { * * @see {@link orElse} for the simpler variant without source tracking * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -725,6 +748,7 @@ export const orElseResult: { * * @see {@link orElse} for a two-option fallback * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -767,6 +791,7 @@ export const firstSomeOf = > = Iterable( * @see {@link fromNullishOr} to treat both `null` and `undefined` as absent * @see {@link fromNullOr} to only treat `null` as absent * + * @stability stable * @category converting * @since 4.0.0 */ @@ -835,6 +861,7 @@ export const fromUndefinedOr = ( * @see {@link fromNullishOr} to treat both `null` and `undefined` as absent * @see {@link fromUndefinedOr} to only treat `undefined` as absent * + * @stability stable * @category converting * @since 4.0.0 */ @@ -874,6 +901,7 @@ export const fromNullOr = ( * @see {@link fromNullishOr} for converting a single value * @see {@link liftThrowable} for functions that throw instead * + * @stability stable * @category converting * @since 4.0.0 */ @@ -906,6 +934,7 @@ export const liftNullishOr = , B>( * @see {@link getOrUndefined} to return `undefined` instead * @see {@link getOrElse} for a custom fallback * + * @stability stable * @category getters * @since 2.0.0 */ @@ -936,6 +965,7 @@ export const getOrNull: (self: Option) => A | null = getOrElse(constNull) * @see {@link getOrNull} to return `null` instead * @see {@link getOrElse} for a custom fallback * + * @stability stable * @category getters * @since 2.0.0 */ @@ -966,6 +996,7 @@ export const getOrUndefined: (self: Option) => A | undefined = getOrElse(c * * @see {@link liftNullishOr} for nullable-returning functions * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1010,6 +1041,7 @@ export const liftThrowable = , B>( * @see {@link getOrThrow} for a version with a default error * @see {@link getOrElse} for a non-throwing alternative * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1053,6 +1085,7 @@ export const getOrThrowWith: { * @see {@link getOrThrowWith} for a custom error * @see {@link getOrElse} for a non-throwing alternative * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1084,6 +1117,7 @@ export const getOrThrow: (self: Option) => A = getOrThrowWith(() => new Er * @see {@link flatMap} when `f` returns an `Option` * @see {@link as} to replace the value with a constant * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1115,6 +1149,7 @@ export const map: { * @see {@link asVoid} to replace with `undefined` * @see {@link map} for a general transformation * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1143,6 +1178,7 @@ export const as: { * * @see {@link as} to replace with a specific constant * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1167,6 +1203,7 @@ export { * * @see {@link asVoid} to convert an existing `Option` to `Option` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1212,6 +1249,7 @@ export { * @see {@link andThen} for a more flexible variant * @see {@link flatten} to unwrap a nested `Option>` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1257,6 +1295,7 @@ export const flatMap: { * @see {@link flatMap} for the standard monadic bind * @see {@link map} when you always return a plain value * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1313,6 +1352,7 @@ export const andThen: { * @see {@link flatMap} when the function already returns `Option` * @see {@link fromNullishOr} for single-value conversion * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -1349,6 +1389,7 @@ export const flatMapNullishOr: { * * @see {@link flatMap} which is `map` + `flatten` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1379,6 +1420,7 @@ export const flatten: (self: Option>) => Option = flatMap(identi * @see {@link zipLeft} to keep the first value instead * @see {@link zipWith} to combine both values * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -1412,6 +1454,7 @@ export const zipRight: { * @see {@link zipRight} to keep the second value instead * @see {@link zipWith} to combine both values * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -1453,6 +1496,7 @@ export const zipLeft: { * * @see {@link flatMap} for single-step chaining * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1491,6 +1535,7 @@ export const composeK: { * @see {@link flatMap} when you want to transform the value * @see {@link filter} for predicate-based filtering * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1525,6 +1570,7 @@ export const tap: { * @see {@link zipWith} to combine with a function instead of a tuple * @see {@link all} to combine many `Option`s * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1566,6 +1612,7 @@ export const product: { * @see {@link product} for combining exactly two * @see {@link all} for tuples, structs, and iterables * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1622,6 +1669,7 @@ export const productMany: { * @see {@link product} for combining exactly two * @see {@link productMany} for a homogeneous collection * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1685,6 +1733,7 @@ export const all: > | Record b + a)) // => 3 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -1764,6 +1814,7 @@ export const reduceCompact: { * * @see {@link fromIterable} for the inverse direction * + * @stability stable * @category converting * @since 2.0.0 */ @@ -1800,6 +1851,7 @@ export const toArray = (self: Option): Array => isNone(self) ? [] : [se * * @see {@link filter} for simple predicate-based filtering * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1843,6 +1895,7 @@ export const partitionMap: { * * @see {@link filter} for predicate-based filtering * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1889,6 +1942,7 @@ export const filterMap: { * @see {@link filterMap} to transform and filter simultaneously * @see {@link exists} to test without filtering * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -1929,6 +1983,7 @@ export const filter: { * eq(Option.none(), Option.none()) // => true * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -1962,6 +2017,7 @@ export const makeEquivalence = (isEquivalent: Equivalence.Equivalence): Eq * ord(Option.some(1), Option.some(2)) // => -1 * ``` * + * @stability stable * @category sorting * @since 4.0.0 */ @@ -1994,6 +2050,7 @@ export const makeOrder = (O: Order): Order> => * * @see {@link zipWith} for a non-lifted variant * + * @stability stable * @category lifting * @since 2.0.0 */ @@ -2031,6 +2088,7 @@ export const lift2 = (f: (a: A, b: B) => C): { * @see {@link filter} to apply a predicate to an existing `Option` * @see {@link toRefinement} for the inverse direction * + * @stability stable * @category lifting * @since 2.0.0 */ @@ -2078,6 +2136,7 @@ export const liftPredicate: { // Note: I intentionally avoid using the NoInfer p * * @see {@link contains} for a version using default equality * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2113,6 +2172,7 @@ export const containsWith = (isEquivalent: (self: A, that: A) => boolean): { * @see {@link containsWith} for custom equality * @see {@link exists} to test with a predicate * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2150,6 +2210,7 @@ export const contains: { * @see {@link filter} to keep or discard based on a predicate * @see {@link contains} to test for a specific value * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -2194,6 +2255,7 @@ export const exists: { * @see {@link bind} to add `Option` values * @see {@link let_ let} to add plain values * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2240,6 +2302,7 @@ export { * @see {@link bind} to add `Option` values * @see {@link bindTo} to start by naming an existing `Option` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -2272,6 +2335,7 @@ export { * @see {@link let_ let} to add plain values * @see {@link bindTo} to start by naming an existing `Option` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2314,6 +2378,7 @@ export const bind: { * @see {@link let_ let} to add plain values * @see {@link bindTo} to start by naming an existing `Option` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -2351,6 +2416,7 @@ export const Do: Option<{}> = some({}) * * @see {@link Do} / {@link bind} for the do notation alternative * + * @stability stable * @category generators * @since 2.0.0 */ @@ -2396,6 +2462,7 @@ export const gen: Gen.Gen = (...args) => { * * @see {@link makeReducerFailFast} for fail-fast semantics * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2434,6 +2501,7 @@ export function makeReducer(combiner: Combiner.Combiner): Reducer.Reducer< * * @see {@link makeReducerFailFast} to get a full `Reducer` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2472,6 +2540,7 @@ export function makeCombinerFailFast(combiner: Combiner.Combiner): Combine * @see {@link makeCombinerFailFast} for just the combiner * @see {@link makeReducer} for non-fail-fast semantics * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Order.ts b/packages/effect/src/Order.ts similarity index 98% rename from repos/effect/packages/effect/src/Order.ts rename to packages/effect/src/Order.ts index 211b44bbd..fdf0c2f54 100644 --- a/repos/effect/packages/effect/src/Order.ts +++ b/packages/effect/src/Order.ts @@ -8,6 +8,7 @@ * orders, tools for reversing and combining comparisons, tuple and struct * helpers, comparison predicates, clamping, and reducer support. * + * @stability stable * @since 2.0.0 */ import { dual } from "./Function.ts" @@ -47,6 +48,7 @@ import * as Reducer from "./Reducer.ts" * * @see {@link make} to create an order from a comparison function * @see {@link Ordering} for the result type of comparisons + * @stability stable * @category models * @since 2.0.0 */ @@ -66,6 +68,7 @@ export interface Order { * This is type-level only, has no runtime representation, and is used * internally by the Effect type system. * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -105,6 +108,7 @@ export interface OrderTypeLambda extends TypeLambda { * * @see {@link mapInput} to transform an order by mapping the input type * @see {@link combine} to combine multiple orders + * @stability stable * @category constructors * @since 2.0.0 */ @@ -138,6 +142,7 @@ export function make( * * @see {@link mapInput} to compare objects by a string property * @see {@link Struct} to combine with other orders for struct comparison + * @stability stable * @category instances * @since 4.0.0 */ @@ -171,6 +176,7 @@ export const String: Order = make((self, that) => self < that ? -1 : 1) * * @see {@link mapInput} to compare objects by a number property * @see {@link BigInt} for bigint comparisons + * @stability stable * @category instances * @since 4.0.0 */ @@ -203,6 +209,7 @@ export const Number: Order = make((self, that) => { * ``` * * @see {@link mapInput} to compare objects by a boolean property + * @stability stable * @category instances * @since 4.0.0 */ @@ -232,6 +239,7 @@ export const Boolean: Order = make((self, that) => self < that ? -1 : 1 * * @see {@link Number} for regular number comparisons * @see {@link mapInput} to compare objects by a bigint property + * @stability stable * @category instances * @since 4.0.0 */ @@ -263,6 +271,7 @@ export const BigInt: Order = make((self, that) => self < that ? -1 : 1) * ``` * * @see {@link combine} to combine orders for multi-criteria comparison + * @stability stable * @category combinators * @since 4.0.0 */ @@ -309,6 +318,7 @@ export function flip(O: Order): Order { * * @see {@link combineAll} to combine multiple orders from a collection * @see {@link mapInput} to transform orders to work with different types + * @stability stable * @category combining * @since 2.0.0 */ @@ -349,6 +359,7 @@ export const combine: { * ``` * * @see {@link combine} to combine with other orders + * @stability stable * @category constructors * @since 4.0.0 */ @@ -394,6 +405,7 @@ export function alwaysEqual(): Order { * * @see {@link combine} to combine two orders * @see {@link makeReducer} to create a reducer for combining orders + * @stability stable * @category combining * @since 2.0.0 */ @@ -440,6 +452,7 @@ export function combineAll(collection: Iterable>): Order { * * @see {@link combine} to combine mapped orders for multi-criteria comparison * @see {@link Struct} to create orders for structs with multiple fields + * @stability stable * @category mapping * @since 2.0.0 */ @@ -478,6 +491,7 @@ export const mapInput: { * ``` * * @see {@link mapInput} to compare objects by a date property + * @stability stable * @category instances * @since 2.0.0 */ @@ -509,6 +523,7 @@ export const Date: Order = mapInput(Number, (date) => date.getTime()) * ``` * * @see {@link Array} to compare arrays with length consideration + * @stability stable * @category combinators * @since 4.0.0 */ @@ -574,6 +589,7 @@ export { * ``` * * @see {@link Tuple} for type-safe tuple ordering + * @stability stable * @category combinators * @since 4.0.0 */ @@ -614,6 +630,7 @@ export { * * @see {@link combine} to combine orders manually * @see {@link mapInput} to extract and compare by a single property + * @stability stable * @category combinators * @since 4.0.0 */ @@ -658,6 +675,7 @@ export function Struct }>( * * @see {@link isLessThanOrEqualTo} for non-strict less than or equal * @see {@link isGreaterThan} for strict greater than + * @stability stable * @category predicates * @since 4.0.0 */ @@ -692,6 +710,7 @@ export const isLessThan = (O: Order): { * * @see {@link isGreaterThanOrEqualTo} for non-strict greater than or equal * @see {@link isLessThan} for strict less than + * @stability stable * @category predicates * @since 4.0.0 */ @@ -726,6 +745,7 @@ export const isGreaterThan = (O: Order): { * * @see {@link isLessThan} for strict less than * @see {@link isGreaterThan} for strict greater than + * @stability stable * @category predicates * @since 4.0.0 */ @@ -761,6 +781,7 @@ export const isLessThanOrEqualTo = (O: Order): { * * @see {@link isGreaterThan} for strict greater than * @see {@link isLessThanOrEqualTo} for less than or equal + * @stability stable * @category predicates * @since 4.0.0 */ @@ -796,6 +817,7 @@ export const isGreaterThanOrEqualTo = (O: Order): { * * @see {@link max} for the maximum of two values * @see {@link clamp} to clamp a value between min and max + * @stability stable * @category comparisons * @since 2.0.0 */ @@ -831,6 +853,7 @@ export const min = (O: Order): { * * @see {@link min} for the minimum of two values * @see {@link clamp} to clamp a value between min and max + * @stability stable * @category comparisons * @since 2.0.0 */ @@ -869,6 +892,7 @@ export const max = (O: Order): { * @see {@link min} for the minimum of two values * @see {@link max} for the maximum of two values * @see {@link isBetween} to check if a value is within a range + * @stability stable * @category comparisons * @since 2.0.0 */ @@ -922,6 +946,7 @@ export const clamp = (O: Order): { * @see {@link clamp} to clamp a value to a range * @see {@link isLessThanOrEqualTo} for less than or equal check * @see {@link isGreaterThanOrEqualTo} for greater than or equal check + * @stability stable * @category predicates * @since 4.0.0 */ @@ -972,6 +997,7 @@ export const isBetween = (O: Order): { * @see {@link combine} to combine two orders * @see {@link combineAll} to combine multiple orders * @see {@link Reducer} for reducing orders as a collection operation + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Ordering.ts b/packages/effect/src/Ordering.ts similarity index 97% rename from repos/effect/packages/effect/src/Ordering.ts rename to packages/effect/src/Ordering.ts index ff93ffca7..892adba14 100644 --- a/repos/effect/packages/effect/src/Ordering.ts +++ b/packages/effect/src/Ordering.ts @@ -5,6 +5,7 @@ * provides helpers for reversing an ordering, matching on the three cases, and * combining ordered comparison results with a reducer. * + * @stability stable * @since 2.0.0 */ import type { LazyArg } from "./Function.ts" @@ -47,6 +48,7 @@ import * as Reducer_ from "./Reducer.ts" * } * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -89,6 +91,7 @@ export type Ordering = -1 | 0 | 1 * } * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -117,6 +120,7 @@ export const reverse = (o: Ordering): Ordering => (o === -1 ? 1 : o === 1 ? -1 : * toMessage(1) // => "greater than" * ``` * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -163,6 +167,7 @@ export const match: { * `combineAll` stops consuming the iterable as soon as it finds a non-zero * `Ordering`. * + * @stability stable * @category ordering * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/PartitionedSemaphore.ts b/packages/effect/src/PartitionedSemaphore.ts similarity index 91% rename from repos/effect/packages/effect/src/PartitionedSemaphore.ts rename to packages/effect/src/PartitionedSemaphore.ts index 0f8a80628..93fdb84f3 100644 --- a/repos/effect/packages/effect/src/PartitionedSemaphore.ts +++ b/packages/effect/src/PartitionedSemaphore.ts @@ -5,6 +5,7 @@ * groups of work compete for the same bounded resource and each group should * make progress without one busy group monopolizing released permits. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "./Effect.ts" @@ -23,6 +24,7 @@ import * as Option from "./Option.ts" * This marker is part of the runtime representation of partitioned semaphore * values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -40,6 +42,7 @@ export const PartitionedTypeId: PartitionedTypeId = "~effect/PartitionedSemaphor * Use this type when declaring fields that must contain the exact * `PartitionedTypeId` marker value. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -58,6 +61,7 @@ export type PartitionedTypeId = "~effect/PartitionedSemaphore" * * Waiting permits are distributed across partitions in round-robin order. * + * @stability unstable * @category models * @since 3.19.4 */ @@ -90,6 +94,7 @@ export interface PartitionedSemaphore { * provides an alternate exported name for APIs that refer to a partitioned * permit pool. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -111,6 +116,7 @@ export interface Partitioned extends PartitionedSemaphore {} * * @see {@link make} for creating a partitioned semaphore inside `Effect` * + * @stability unstable * @category constructors * @since 3.19.4 */ @@ -142,36 +148,51 @@ export const makeUnsafe = (options: { const partitions = MutableHashMap.empty>() let iterator = partitions[Symbol.iterator]() + let releasing = false + let pendingPermits = 0 const releaseUnsafe = (permits: number): number => { - while (permits > 0) { - if (waitingPermits === 0) { - totalPermits = Math.min(maxPermits, totalPermits + permits) - return totalPermits - } + if (permits > 0) { + pendingPermits += permits + } - let state = iterator.next() - if (state.done) { - iterator = partitions[Symbol.iterator]() - state = iterator.next() - if (state.done) { - return totalPermits - } - } + // Synchronous waiter finalizers can release again. Let the active loop + // allocate those permits rather than recursively resuming another fiber. + if (!releasing) { + releasing = true + try { + while (pendingPermits > 0 && waitingPermits > 0) { + let state = iterator.next() + if (state.done) { + iterator = partitions[Symbol.iterator]() + state = iterator.next() + if (state.done) { + break + } + } - const waiter = state.value[1].values().next().value - if (waiter === undefined) { - continue - } + const waiter = state.value[1].values().next().value + if (waiter === undefined) { + continue + } - waiter.permits -= 1 - waitingPermits -= 1 + waiter.permits -= 1 + waitingPermits -= 1 + pendingPermits -= 1 - if (waiter.permits === 0) { - waiter.resume() + if (waiter.permits === 0) { + waiter.resume() + } + } + } finally { + releasing = false } + } - permits -= 1 + // Free permits must be visible before a reentrant release returns. + if (waitingPermits === 0) { + totalPermits = Math.min(maxPermits, totalPermits + pendingPermits) + pendingPermits = 0 } return totalPermits @@ -328,6 +349,7 @@ export const makeUnsafe = (options: { * * @see {@link makeUnsafe} for synchronous construction * + * @stability unstable * @category constructors * @since 3.19.4 */ @@ -356,6 +378,7 @@ export const make = (options: { * @see {@link release} for returning permits to the shared pool * @see {@link withPermitsIfAvailable} for running only when permits are immediately available * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -375,6 +398,7 @@ export const available = (self: PartitionedSemaphore): Effect.Effect(self: PartitionedSemaphore): number => self.capac * @see {@link withPermits} for automatic acquire and release around an effect * @see {@link withPermit} for acquiring exactly one permit around an effect * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -431,6 +456,7 @@ export const take: { * @see {@link withPermits} for automatic acquire and release around an effect * @see {@link available} for reading the permit count without releasing * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -465,6 +491,7 @@ export const release: { * @see {@link take} for manual acquisition * @see {@link release} for manual release * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -509,6 +536,7 @@ export const withPermits: { * @see {@link take} for manual acquisition * @see {@link release} for manual release * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -547,6 +575,7 @@ export const withPermit: { * @see {@link withPermits} for the keyed variant that waits until permits are * available for a partition * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Path.ts b/packages/effect/src/Path.ts similarity index 99% rename from repos/effect/packages/effect/src/Path.ts rename to packages/effect/src/Path.ts index 946b18f0a..aca7dfc35 100644 --- a/repos/effect/packages/effect/src/Path.ts +++ b/packages/effect/src/Path.ts @@ -7,6 +7,7 @@ * file URLs. This module includes the service interface, parsed path type, * service tag, runtime marker, and built-in POSIX path layer. * + * @stability unstable * @since 4.0.0 */ import * as Context from "./Context.ts" @@ -26,6 +27,7 @@ import { BadArgument } from "./PlatformError.ts" * * @see {@link layer} for the built-in POSIX `Path` service layer * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -78,6 +80,7 @@ export const TypeId = "~effect/Path" * result.resolved // => "/base/relative/path" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -133,6 +136,7 @@ export interface Path { * Effect.runSync(Effect.provide(program, Path.layer)) // => ["file.txt", "file.txt"] * ``` * + * @stability unstable * @since 4.0.0 */ export declare namespace Path { @@ -249,6 +253,7 @@ export declare namespace Path { * Effect.runSync(Effect.provide(program, customPathLayer)) // => "home/user/file.txt" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -861,6 +866,7 @@ const posixImpl = Path.of({ * * @see {@link Path} for accessing the `Path` service from an effect * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Pipeable.ts b/packages/effect/src/Pipeable.ts similarity index 98% rename from repos/effect/packages/effect/src/Pipeable.ts rename to packages/effect/src/Pipeable.ts index 6f2697e74..f6be3cdb7 100644 --- a/repos/effect/packages/effect/src/Pipeable.ts +++ b/packages/effect/src/Pipeable.ts @@ -8,6 +8,7 @@ * to compose transformations, validations, and effectful operations while * keeping the original value as the starting point of the pipeline. * + * @stability stable * @since 2.0.0 */ @@ -38,6 +39,7 @@ * Effect.runSync(program) // => 4 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -558,6 +560,7 @@ export interface Pipeable { * result // => 21 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -601,6 +604,7 @@ export const pipeArguments = (self: A, args: IArguments): unknown => { * Use when classes or object prototypes can reuse this value when they need the * standard pipe implementation backed by `pipeArguments`. * + * @stability stable * @category prototypes * @since 3.15.0 */ @@ -619,6 +623,7 @@ export const Prototype: Pipeable = { * Use when you need to define a class that supports Effect-style method * chaining through `.pipe(...)`. * + * @stability stable * @category constructors * @since 3.15.0 */ @@ -640,6 +645,7 @@ export const Class: new() => Pipeable = (function() { * @see {@link Class} for the base constructor * @see {@link Mixin} for wrapping an existing class constructor * + * @stability stable * @category models * @since 3.15.0 */ @@ -663,6 +669,7 @@ export interface PipeableConstructor { * * @see {@link Prototype} for a reusable prototype object * @see {@link Class} for a base constructor to extend + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/PlatformError.ts b/packages/effect/src/PlatformError.ts similarity index 97% rename from repos/effect/packages/effect/src/PlatformError.ts rename to packages/effect/src/PlatformError.ts index 4a5f1681a..bc84567bd 100644 --- a/repos/effect/packages/effect/src/PlatformError.ts +++ b/packages/effect/src/PlatformError.ts @@ -7,6 +7,7 @@ * operating system, while preserving useful details such as the module, method, * path, descriptor, description, and original cause when available. * + * @stability unstable * @since 4.0.0 */ import * as Data from "./Data.ts" @@ -31,6 +32,7 @@ const TypeId = "~effect/PlatformError" * @see {@link SystemError} for failures reported by the host platform or operating system * @see {@link PlatformError} for the wrapper used by most platform APIs * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -70,6 +72,7 @@ export class BadArgument extends Data.TaggedError("BadArgument")<{ * @see {@link SystemError} for the error data that carries this tag on its `_tag` field * @see {@link systemError} for creating a `PlatformError` from a system failure with one of these tags * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -104,6 +107,7 @@ export type SystemErrorTag = * @see {@link BadArgument} for platform API failures caused by rejected caller input before an operation runs * @see {@link SystemErrorTag} for the normalized tag values stored in `_tag` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -152,6 +156,7 @@ export class SystemError extends Data.Error<{ * @see {@link badArgument} for creating this wrapper from rejected caller input * @see {@link systemError} for creating this wrapper from a host or operating-system failure * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -204,6 +209,7 @@ export class PlatformError extends Data.TaggedError("PlatformError")<{ * PlatformError.isPlatformError(new Error("boom")) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -217,6 +223,7 @@ export const isPlatformError = (u: unknown): u is PlatformError => Predicate.has * Use to adapt an operating-system or platform failure into the normalized * platform error model. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -238,6 +245,7 @@ export const systemError = (options: { * Use to report a platform API rejecting caller input before performing the * underlying operation. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Pool.ts b/packages/effect/src/Pool.ts similarity index 99% rename from repos/effect/packages/effect/src/Pool.ts rename to packages/effect/src/Pool.ts index 2ee2e30cb..123e00371 100644 --- a/repos/effect/packages/effect/src/Pool.ts +++ b/packages/effect/src/Pool.ts @@ -7,6 +7,7 @@ * pools, pools that resize with a time-to-live policy, custom strategy pools, * per-item concurrency limits, and runtime state types used by pool strategies. * + * @stability stable * @since 2.0.0 */ import type * as Cause from "./Cause.ts" @@ -54,6 +55,7 @@ interface PoolImpl extends Pool { * @see {@link get} for acquiring an item from a pool * @see {@link invalidate} for removing a broken item from the pool * + * @stability stable * @category models * @since 2.0.0 */ @@ -80,6 +82,7 @@ export interface Pool extends Pipeable { * @see {@link State} for mutable runtime state instead of static configuration * @see {@link Strategy} for the resizing and reclamation contract stored on the config * + * @stability stable * @category models * @since 4.0.0 */ @@ -112,6 +115,7 @@ export interface Config { * @see {@link get} for acquiring items through the high-level API * @see {@link invalidate} for invalidating items through the high-level API * + * @stability stable * @category models * @since 4.0.0 */ @@ -145,6 +149,7 @@ export interface State { * @see {@link Strategy} for the custom strategy callbacks that receive and return pool items * @see {@link State} for the runtime sets that store active, available, and invalidated pool items * + * @stability stable * @category models * @since 4.0.0 */ @@ -178,6 +183,7 @@ export interface PoolItem { * * @see {@link makeWithStrategy} for constructing a pool from a custom `Strategy` * + * @stability stable * @category models * @since 4.0.0 */ @@ -199,6 +205,7 @@ export interface Strategy { * * This predicate narrows the input to `Pool`. * + * @stability stable * @category guards * @since 2.0.0 */ @@ -229,6 +236,7 @@ export const isPool = (u: unknown): u is Pool => hasProperty(u * * @see {@link makeWithTTL} for pools with min/max sizes and a TTL-based shrinking policy * @see {@link makeWithStrategy} for pools with a custom resizing and reclamation strategy + * @stability stable * @category constructors * @since 2.0.0 */ @@ -298,6 +306,7 @@ export const make = (options: { * await Effect.runPromise(program) // => ["executed: select 1"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -334,6 +343,7 @@ export const makeWithTTL = (options: { * @see {@link makeWithTTL} for min/max pools that shrink excess items with a TTL policy * @see {@link Strategy} for the custom strategy contract consumed by this constructor * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -418,7 +428,6 @@ const shutdown = Effect.fnUntraced(function*(self: Pool) { yield* item.finalizer } } - yield* semaphore.releaseAll if (self.state.waiters.size > 0) { const waiters = Array.from(self.state.waiters) self.state.waiters.clear() @@ -446,6 +455,7 @@ const shutdown = Effect.fnUntraced(function*(self: Pool) { * * @see {@link invalidate} for removing an unhealthy item from future reuse * + * @stability stable * @category getters * @since 2.0.0 */ @@ -488,6 +498,7 @@ export const get = (self: Pool): Effect.Effect => * * @see {@link get} for borrowing an item for the lifetime of a scope * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -768,6 +779,7 @@ const removeAvailable = (self: Pool, item: PoolItem): void => * * @see {@link get} for retrieving scoped items from the pool * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -794,6 +806,7 @@ export const invalidate: { * * @see {@link get} for acquiring an item * + * @stability stable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Predicate.ts b/packages/effect/src/Predicate.ts similarity index 97% rename from repos/effect/packages/effect/src/Predicate.ts rename to packages/effect/src/Predicate.ts index f66458aaf..dfab63412 100644 --- a/repos/effect/packages/effect/src/Predicate.ts +++ b/packages/effect/src/Predicate.ts @@ -7,6 +7,7 @@ * property and tag checks, tuple and struct checks, boolean combinators, and * helpers for composing predicates and refinements. * + * @stability stable * @since 2.0.0 */ import { dual } from "./Function.ts" @@ -40,6 +41,7 @@ import type { TupleOf, TupleOfAtLeast } from "./Types.ts" * @see {@link Refinement} * @see {@link mapInput} * @see {@link and} + * @stability stable * @category models * @since 2.0.0 */ @@ -73,6 +75,7 @@ export interface Predicate { * ``` * * @see {@link Predicate} + * @stability stable * @category utility types * @since 2.0.0 */ @@ -110,6 +113,7 @@ export interface PredicateTypeLambda extends TypeLambda { * @see {@link Predicate} * @see {@link compose} * @see {@link isString} + * @stability stable * @category models * @since 2.0.0 */ @@ -143,6 +147,7 @@ export interface Refinement { * * @see {@link Predicate} * @see {@link Refinement} + * @stability stable * @since 3.6.0 */ export declare namespace Predicate { @@ -233,6 +238,7 @@ export declare namespace Predicate { * * @see {@link Refinement} * @see {@link Predicate} + * @stability stable * @since 3.6.0 */ export declare namespace Refinement { @@ -354,6 +360,7 @@ export declare namespace Refinement { * @see {@link Predicate} * @see {@link and} * @see {@link not} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -387,6 +394,7 @@ export const mapInput: { * * @see {@link isTupleOfAtLeast} * @see {@link Tuple} + * @stability stable * @category guards * @since 3.3.0 */ @@ -420,6 +428,7 @@ export const isTupleOf: { * * @see {@link isTupleOf} * @see {@link Tuple} + * @stability stable * @category guards * @since 3.3.0 */ @@ -452,6 +461,7 @@ export const isTupleOfAtLeast: { * * @see {@link isNullish} * @see {@link isNotNullish} + * @stability stable * @category predicates * @since 2.0.0 */ @@ -484,6 +494,7 @@ export function isTruthy(input: unknown): boolean { * * @see {@link isMap} * @see {@link isIterable} + * @stability stable * @category guards * @since 2.0.0 */ @@ -516,6 +527,7 @@ export function isSet(input: unknown): input is Set { * * @see {@link isSet} * @see {@link isIterable} + * @stability stable * @category guards * @since 2.0.0 */ @@ -550,6 +562,7 @@ export function isMap(input: unknown): input is Map { * @see {@link isNumber} * @see {@link isBoolean} * @see {@link Refinement} + * @stability stable * @category guards * @since 2.0.0 */ @@ -583,6 +596,7 @@ export function isString(input: unknown): input is string { * * @see {@link isBigInt} * @see {@link isString} + * @stability stable * @category guards * @since 2.0.0 */ @@ -616,6 +630,7 @@ export function isNumber(input: unknown): input is number { * * @see {@link isString} * @see {@link isNumber} + * @stability stable * @category guards * @since 2.0.0 */ @@ -648,6 +663,7 @@ export function isBoolean(input: unknown): input is boolean { * ``` * * @see {@link isNumber} + * @stability stable * @category guards * @since 2.0.0 */ @@ -680,6 +696,7 @@ export function isBigInt(input: unknown): input is bigint { * ``` * * @see {@link isPropertyKey} + * @stability stable * @category guards * @since 2.0.0 */ @@ -715,6 +732,7 @@ export function isSymbol(input: unknown): input is symbol { * @see {@link isString} * @see {@link isNumber} * @see {@link isSymbol} + * @stability stable * @category guards * @since 4.0.0 */ @@ -747,6 +765,7 @@ export function isPropertyKey(u: unknown): u is PropertyKey { * ``` * * @see {@link isObjectKeyword} + * @stability stable * @category guards * @since 2.0.0 */ @@ -778,6 +797,7 @@ export function isFunction(input: unknown): input is Function { * * @see {@link isNotUndefined} * @see {@link isNullish} + * @stability stable * @category guards * @since 2.0.0 */ @@ -808,6 +828,7 @@ export function isUndefined(input: unknown): input is undefined { * * @see {@link isUndefined} * @see {@link isNotNullish} + * @stability stable * @category guards * @since 2.0.0 */ @@ -838,6 +859,7 @@ export function isNotUndefined(input: A): input is Exclude { * * @see {@link isNotNull} * @see {@link isNullish} + * @stability stable * @category guards * @since 2.0.0 */ @@ -868,6 +890,7 @@ export function isNull(input: unknown): input is null { * * @see {@link isNull} * @see {@link isNotNullish} + * @stability stable * @category guards * @since 2.0.0 */ @@ -898,6 +921,7 @@ export function isNotNull(input: A): input is Exclude { * @see {@link isNotNullish} * @see {@link isUndefined} * @see {@link isNull} + * @stability stable * @category guards * @since 4.0.0 */ @@ -929,6 +953,7 @@ export function isNullish(input: A): input is A & (null | undefined) { * @see {@link isNullish} * @see {@link isNotNull} * @see {@link isNotUndefined} + * @stability stable * @category guards * @since 4.0.0 */ @@ -952,6 +977,7 @@ export function isNotNullish(input: A): input is NonNullable { * ``` * * @see {@link isUnknown} + * @stability stable * @category guards * @since 2.0.0 */ @@ -975,6 +1001,7 @@ export function isNever(_: unknown): _ is never { * ``` * * @see {@link isNever} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1004,6 +1031,7 @@ export function isUnknown(_: unknown): _ is unknown { * * @see {@link isObject} * @see {@link isObjectKeyword} + * @stability stable * @category guards * @since 4.0.0 */ @@ -1036,6 +1064,7 @@ export function isObjectOrArray(input: unknown): input is { [x: PropertyKey]: un * * @see {@link isObjectOrArray} * @see {@link isReadonlyObject} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1069,6 +1098,7 @@ export function isObject(input: unknown): input is { [x: PropertyKey]: unknown } * ``` * * @see {@link isObject} + * @stability stable * @category guards * @since 4.0.0 */ @@ -1099,6 +1129,7 @@ export function isReadonlyObject(input: unknown): input is { readonly [x: Proper * * @see {@link isObject} * @see {@link isObjectOrArray} + * @stability stable * @category guards * @since 4.0.0 */ @@ -1134,6 +1165,7 @@ export function isObjectKeyword(input: unknown): input is object { * * @see {@link isTagged} * @see {@link isObjectKeyword} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1169,6 +1201,7 @@ export const hasProperty: { * ``` * * @see {@link hasProperty} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1202,6 +1235,7 @@ export const isTagged: { * ``` * * @see {@link isUnknown} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1232,6 +1266,7 @@ export function isError(input: unknown): input is Error { * * @see {@link isIterable} * @see {@link isSet} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1261,6 +1296,7 @@ export function isUint8Array(input: unknown): input is Uint8Array { * ``` * * @see {@link isRegExp} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1291,6 +1327,7 @@ export function isDate(input: unknown): input is Date { * * @see {@link isSet} * @see {@link isMap} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1320,6 +1357,7 @@ export function isIterable(input: unknown): input is Iterable { * ``` * * @see {@link isPromiseLike} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1350,6 +1388,7 @@ export function isPromise(input: unknown): input is Promise { * ``` * * @see {@link isPromise} + * @stability stable * @category guards * @since 2.0.0 */ @@ -1379,6 +1418,7 @@ export function isPromiseLike(input: unknown): input is PromiseLike { * ``` * * @see {@link isDate} + * @stability stable * @category guards * @since 3.9.0 */ @@ -1414,6 +1454,7 @@ export function isRegExp(input: unknown): input is RegExp { * * @see {@link and} * @see {@link Refinement} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1453,6 +1494,7 @@ export const compose: { * * @see {@link Struct} * @see {@link isTupleOf} + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1502,6 +1544,7 @@ export function Tuple>( * * @see {@link Tuple} * @see {@link hasProperty} + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1548,6 +1591,7 @@ export function Struct>( * @see {@link and} * @see {@link or} * @see {@link xor} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1580,6 +1624,7 @@ export function not(self: Predicate): Predicate { * * @see {@link and} * @see {@link xor} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1626,6 +1671,7 @@ export const or: { * * @see {@link or} * @see {@link not} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1661,6 +1707,7 @@ export const and: { * * @see {@link or} * @see {@link and} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1692,6 +1739,7 @@ export const xor: { * ``` * * @see {@link xor} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1727,6 +1775,7 @@ export const eqv: { * * @see {@link and} * @see {@link or} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1761,6 +1810,7 @@ export const implies: { * * @see {@link or} * @see {@link not} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1795,6 +1845,7 @@ export const nor: { * * @see {@link and} * @see {@link not} + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1830,6 +1881,7 @@ export const nand: { * * @see {@link some} * @see {@link and} + * @stability stable * @category combining * @since 2.0.0 */ @@ -1868,6 +1920,7 @@ export function every(collection: Iterable>): Predicate { * * @see {@link every} * @see {@link or} + * @stability stable * @category combining * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/PrimaryKey.ts b/packages/effect/src/PrimaryKey.ts similarity index 97% rename from repos/effect/packages/effect/src/PrimaryKey.ts rename to packages/effect/src/PrimaryKey.ts index f7ddfa656..77551232c 100644 --- a/repos/effect/packages/effect/src/PrimaryKey.ts +++ b/packages/effect/src/PrimaryKey.ts @@ -4,6 +4,7 @@ * method at {@link symbol}; consumers can check unknown values with * {@link isPrimaryKey} and read the key with {@link value}. * + * @stability stable * @since 2.0.0 */ @@ -21,6 +22,7 @@ import { hasProperty } from "./Predicate.ts" * @see {@link value} for reading the string key from a `PrimaryKey` value * @see {@link isPrimaryKey} for checking whether an unknown value carries this method * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -56,6 +58,7 @@ export const symbol = "~effect/PrimaryKey" * PrimaryKey.value(productId) // => "electronics-42" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -81,6 +84,7 @@ export interface PrimaryKey { * @see {@link PrimaryKey} for the protocol being checked * @see {@link value} for extracting the string value after narrowing * + * @stability stable * @category guards * @since 4.0.0 */ @@ -117,6 +121,7 @@ export const isPrimaryKey = (u: unknown): u is PrimaryKey => hasProperty(u, symb * PrimaryKey.value(simpleKey) // => "simple-key-123" * ``` * + * @stability stable * @category getters * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/PubSub.ts b/packages/effect/src/PubSub.ts similarity index 96% rename from repos/effect/packages/effect/src/PubSub.ts rename to packages/effect/src/PubSub.ts index e8a8e5a83..15ab11e10 100644 --- a/repos/effect/packages/effect/src/PubSub.ts +++ b/packages/effect/src/PubSub.ts @@ -8,6 +8,7 @@ * subscribers, message-taking helpers, capacity and shutdown operations, a * type guard, and low-level types for custom hub strategies. * + * @stability stable * @since 2.0.0 */ import * as Arr from "./Array.ts" @@ -60,6 +61,7 @@ const TypeId = "~effect/PubSub" * actual // => ["Hello", "World"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -81,6 +83,7 @@ export interface PubSub extends Pipeable { * `PubSub`, including atomic implementations, backing subscriptions, replay * windows, and delivery strategies. * + * @stability stable * @since 2.0.0 */ export declare namespace PubSub { @@ -162,12 +165,18 @@ export declare namespace PubSub { /** * Describes how publishers should signal to subscribers that they are * waiting for space to become available in the `PubSub`. + * + * **Details** + * + * The publisher may have yielded since `publish` checked `ended`, so it + * must be checked again before registering or publishing the surplus. */ handleSurplus( pubsub: Atomic, subscribers: Subscribers, elements: Iterable, - isShutdown: MutableRef.MutableRef + isShutdown: MutableRef.MutableRef, + ended: MutableRef.MutableRef> ): Effect.Effect /** @@ -223,6 +232,7 @@ export declare namespace PubSub { * actual // => [true, false, false] * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -259,6 +269,7 @@ const SubscriptionTypeId = "~effect/PubSub/Subscription" * actual // => { message: "msg1", messages: ["msg2"], allMessages: ["msg3"] } * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -302,6 +313,7 @@ export interface Subscription extends Pipeable { * actual // => true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -333,6 +345,8 @@ export const make = ( * them. When the capacity is full, publishers suspend until space is available. * Pass an options object to configure both `capacity` and an optional replay * buffer for late subscribers. + * The capacity must be a positive integer or `Infinity` for unbounded storage. + * Invalid capacities cause the effect to die. * * **Example** (Creating a bounded PubSub) * @@ -359,6 +373,7 @@ export const make = ( * actual // => [100, 100] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -379,6 +394,8 @@ export const bounded = ( * * **Details** * + * The capacity must be a positive integer or `Infinity` for unbounded storage. + * Invalid capacities cause the effect to die. * For best performance use capacities that are powers of two. * * **Example** (Dropping messages when full) @@ -406,6 +423,7 @@ export const bounded = ( * actual // => { dropped: true, messages: ["msg1", "msg2", "msg3"] } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -426,6 +444,8 @@ export const dropping = ( * * **Details** * + * The capacity must be a positive integer or `Infinity` for unbounded storage. + * Invalid capacities cause the effect to die. * For best performance use capacities that are powers of two. * * **Example** (Sliding old messages when full) @@ -452,6 +472,7 @@ export const dropping = ( * actual // => ["msg2", "msg3", "msg4"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -492,6 +513,7 @@ export const sliding = ( * actual // => ["message-0", "message-1", "message-2"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -519,8 +541,9 @@ export const unbounded = (options?: { * * **Gotchas** * - * The capacity must be greater than zero; invalid capacities throw - * synchronously before an atomic implementation is created. + * The capacity must be a positive integer or `Infinity` for unbounded storage; + * invalid capacities throw synchronously before an atomic implementation is + * created. * * @see {@link make} for constructing a `PubSub` from an atomic implementation and delivery strategy * @see {@link makeAtomicUnbounded} for an atomic implementation without a bounded capacity @@ -528,6 +551,7 @@ export const unbounded = (options?: { * @see {@link dropping} for the higher-level dropping constructor * @see {@link sliding} for the higher-level sliding constructor * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -569,6 +593,7 @@ export const makeAtomicBounded = ( * @see {@link make} for wrapping an atomic implementation with a delivery strategy * @see {@link unbounded} for the high-level effectful constructor for unbounded `PubSub` values * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -599,6 +624,7 @@ export const makeAtomicUnbounded = (options?: { * actual // => [100, Number.MAX_SAFE_INTEGER] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -639,6 +665,7 @@ export const capacity = (self: PubSub): number => self.pubsub.capacity * actual // => { initialSize: 0, afterPublish: 2, messages: ["msg1", "msg2"] } * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -671,6 +698,7 @@ export const size = (self: PubSub): Effect.Effect => Effect.sync(( * actual // => 0 * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -714,6 +742,7 @@ export const sizeUnsafe = (self: PubSub): number => { * actual // => { initiallyFull: false, nowFull: true, messages: ["msg1", "msg2"] } * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -748,6 +777,7 @@ export const isFull = (self: PubSub): Effect.Effect => * actual // => { initiallyEmpty: true, nowEmpty: false, message: "Hello" } * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -785,6 +815,7 @@ export const isEmpty = (self: PubSub): Effect.Effect => Effect.ma * actual // => { isShutdown: true, published: false } * ``` * + * @stability stable * @category lifecycle * @since 2.0.0 */ @@ -855,6 +886,7 @@ export const shutdown = (self: PubSub): Effect.Effect => * * @see {@link shutdown} for interrupting subscribers instead of delivering a final message * + * @stability stable * @category lifecycle * @since 4.0.0 */ @@ -870,6 +902,7 @@ export const end: { * * See {@link end} for the semantics. * + * @stability stable * @category lifecycle * @since 4.0.0 */ @@ -925,6 +958,7 @@ export const endUnsafe: { * actual // => [false, true] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -955,6 +989,7 @@ export const isShutdown = (self: PubSub): Effect.Effect => Effect * actual // => [false, true] * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -992,6 +1027,7 @@ export const isShutdownUnsafe = (self: PubSub): boolean => self.shutdownFl * actual // => "PubSub has been shutdown!" * ``` * + * @stability stable * @category lifecycle * @since 2.0.0 */ @@ -1036,6 +1072,7 @@ export const awaitShutdown = (self: PubSub): Effect.Effect => self.s * * @see {@link publishUnsafe} for a synchronous non-blocking attempt that does not run effectful surplus handling * + * @stability stable * @category publishing * @since 2.0.0 */ @@ -1057,7 +1094,8 @@ export const publish: { self.pubsub, self.subscribers, [value], - self.shutdownFlag + self.shutdownFlag, + self.ended ) })) @@ -1092,6 +1130,7 @@ export const publish: { * * @see {@link publish} for effectful publishing that honors the configured surplus strategy * + * @stability stable * @category publishing * @since 4.0.0 */ @@ -1139,6 +1178,7 @@ export const publishUnsafe: { * actual // => { allPublished: true, firstBatch: ["msg1", "msg2"], result: true, secondBatch: ["msg3", "msg4"] } * ``` * + * @stability stable * @category publishing * @since 2.0.0 */ @@ -1159,7 +1199,8 @@ export const publishAll: { self.pubsub, self.subscribers, surplus, - self.shutdownFlag + self.shutdownFlag, + self.ended ) })) @@ -1211,6 +1252,7 @@ export const publishAll: { * actual // => [["Hello", "World"], ["Broadcast", "Broadcast"]] * ``` * + * @stability stable * @category subscriptions * @since 2.0.0 */ @@ -1278,6 +1320,7 @@ const unsubscribe = (self: Subscription): Effect.Effect => * actual // => "Hello" * ``` * + * @stability stable * @category subscriptions * @since 4.0.0 */ @@ -1326,6 +1369,7 @@ export const take = (self: Subscription): Effect.Effect => * actual // => ["msg1", "msg2", "msg3"] * ``` * + * @stability stable * @category subscriptions * @since 4.0.0 */ @@ -1352,7 +1396,17 @@ export const takeAll = (self: Subscription): Effect.Effect(self: Subscription) => Effect.callback((resume) => { if (self.shutdownFlag.current) return resume(Effect.interrupt) - if (Option.isSome(self.ended.current)) return resume(Effect.succeed(self.ended.current.value)) + if (Option.isSome(self.ended.current)) { + // Messages may have been published after the empty check, before this callback ran. + const message = self.pollers.length === 0 + ? self.subscription.poll() + : MutableList.Empty + if (message !== MutableList.Empty) { + self.strategy.onPubSubEmptySpaceUnsafe(self.pubsub, self.subscribers) + return resume(Effect.succeed(message)) + } + return resume(Effect.succeed(self.ended.current.value)) + } const deferred = Deferred.makeUnsafe() let set = self.subscribers.get(self.subscription) if (!set) { @@ -1405,6 +1459,7 @@ const pollForItem = (self: Subscription) => * actual // => [["msg1", "msg2", "msg3"], ["msg4", "msg5"], []] * ``` * + * @stability stable * @category subscriptions * @since 4.0.0 */ @@ -1459,6 +1514,7 @@ export const takeUpTo: { * actual // => ["msg1", "msg2", "msg3"] * ``` * + * @stability stable * @category subscriptions * @since 4.0.0 */ @@ -1547,6 +1603,7 @@ const takeRemainderLoop = ( * * @see {@link remainingUnsafe} for a synchronous check that reports shutdown as `Option.none()` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -1583,6 +1640,7 @@ export const remaining = (self: Subscription): Effect.Effect => * * @see {@link remaining} for the effectful variant that interrupts on shutdown * + * @stability stable * @category getters * @since 4.0.0 */ @@ -2470,7 +2528,7 @@ const makePubSubUnsafe = ( ): PubSub => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy, ended) const ensureCapacity = (capacity: number): void => { - if (capacity <= 0) { + if (capacity <= 0 || (capacity !== Infinity && !Number.isInteger(capacity))) { throw new Error(`Cannot construct PubSub with capacity of ${capacity}`) } } @@ -2501,6 +2559,7 @@ const ensureCapacity = (capacity: number): void => { * @see {@link DroppingStrategy} for dropping new messages when capacity is full * @see {@link SlidingStrategy} for evicting old messages when capacity is full * + * @stability stable * @category models * @since 4.0.0 */ @@ -2523,9 +2582,13 @@ export class BackPressureStrategy implements PubSub.Strategy { pubsub: PubSub.Atomic, subscribers: PubSub.Subscribers, elements: Iterable, - isShutdown: MutableRef.MutableRef + isShutdown: MutableRef.MutableRef, + ended: MutableRef.MutableRef> ): Effect.Effect { return Effect.callback((resume) => { + // The publisher may have yielded since `publish` checked `ended`, and + // `end` only rejects publishers that are already registered. + if (Option.isSome(ended.current)) return resume(Effect.succeed(false)) const deferred = Deferred.makeUnsafe() this.offerUnsafe(elements, deferred) this.onPubSubEmptySpaceUnsafe(pubsub, subscribers) @@ -2642,6 +2705,7 @@ export class BackPressureStrategy implements PubSub.Strategy { * actual // => { published: [true, true, false], messages: ["msg1", "msg2"] } * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -2725,6 +2789,7 @@ export class DroppingStrategy implements PubSub.Strategy { * actual // => ["msg3", "msg4"] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -2737,9 +2802,11 @@ export class SlidingStrategy implements PubSub.Strategy { pubsub: PubSub.Atomic, subscribers: PubSub.Subscribers, elements: Iterable, - _isShutdown: MutableRef.MutableRef + _isShutdown: MutableRef.MutableRef, + ended: MutableRef.MutableRef> ): Effect.Effect { return Effect.sync(() => { + if (Option.isSome(ended.current)) return false this.slidingPublishUnsafe(pubsub, elements) this.completeSubscribersUnsafe(pubsub, subscribers) return true diff --git a/repos/effect/packages/effect/src/Pull.ts b/packages/effect/src/Pull.ts similarity index 97% rename from repos/effect/packages/effect/src/Pull.ts rename to packages/effect/src/Pull.ts index 514b66e02..ca31de1e0 100644 --- a/repos/effect/packages/effect/src/Pull.ts +++ b/packages/effect/src/Pull.ts @@ -8,6 +8,7 @@ * filtering, catching, converting, and matching done signals separately from * ordinary failures. * + * @stability stable * @since 4.0.0 */ import * as Cause from "./Cause.ts" @@ -34,6 +35,7 @@ import * as Result from "./Result.ts" * consumers can distinguish ordinary failures from end-of-input and carry a * leftover value when needed. * + * @stability stable * @category models * @since 4.0.0 */ @@ -53,6 +55,7 @@ export interface Pull * @see {@link Leftover} for extracting the completion leftover type * @see {@link Services} for extracting the required services type instead * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -71,6 +74,7 @@ export type Success

= P extends Effect ? _A : n * @see {@link Services} for extracting the required services type instead * @see {@link ExcludeDone} for excluding `Cause.Done` from an error union * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -90,6 +94,7 @@ export type Error

= P extends Effect ? _E exten * @see {@link Error} for extracting the ordinary failure type, excluding `Cause.Done` * @see {@link Services} for extracting the required services type instead * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -108,6 +113,7 @@ export type Leftover

= P extends Effect ? _E ex * @see {@link Error} for extracting the ordinary failure type * @see {@link Leftover} for extracting the completion leftover type * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -124,6 +130,7 @@ export type Services

= P extends Effect ? _R : * @see {@link Error} for extracting ordinary failures from a `Pull` * @see {@link Leftover} for extracting the completion leftover type * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -151,6 +158,7 @@ export type ExcludeDone = Exclude> * @see {@link matchEffect} for handling success, ordinary failure, and done outcomes explicitly * @see {@link filterDoneLeftover} for extracting a done leftover from an existing `Cause` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -180,6 +188,7 @@ export const catchDone: { * @see {@link filterDone} for extracting the `Cause.Done` value from a `Cause` * @see {@link filterNoDone} for selecting causes with no done failures * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -197,6 +206,7 @@ export const isDoneCause = (cause: Cause.Cause): boolean => cause.reasons. * @see {@link isDoneCause} for checking an entire `Cause` for any done reason * @see {@link filterDone} for extracting the `Cause.Done` value from a `Cause` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -221,6 +231,7 @@ export const isDoneFailure = ( * done signal. Without a done signal the `Result` fails with the original * cause. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -261,6 +272,7 @@ export const filterDone: ( * @see {@link filterDoneLeftover} for extracting only the done leftover value * @see {@link filterNoDone} for the inverse filter that succeeds only when no done failure is present * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -284,6 +296,7 @@ export const filterDoneVoid: ( * @see {@link filterDone} for the inverse typed done filter * @see {@link filterDoneVoid} for done detection when the payload is not needed * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -304,6 +317,7 @@ export const filterNoDone: ( * Use to extract only the leftover value carried by a `Cause.Done` completion * signal. * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -333,6 +347,7 @@ export const filterDoneLeftover: ( * @see {@link filterDone} for extracting the done signal without converting the cause to an `Exit` * @see {@link matchEffect} for handling `Pull` success, failure, and done outcomes directly * + * @stability stable * @category converting * @since 4.0.0 */ @@ -364,6 +379,7 @@ export const doneExitFromCause = (cause: Cause.Cause): Exit.Exit "Stream halted with: stream ended" * ``` * + * @stability stable * @category pattern matching * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Queue.ts b/packages/effect/src/Queue.ts similarity index 97% rename from repos/effect/packages/effect/src/Queue.ts rename to packages/effect/src/Queue.ts index 5a1f93afc..6ea790b82 100644 --- a/repos/effect/packages/effect/src/Queue.ts +++ b/packages/effect/src/Queue.ts @@ -6,6 +6,7 @@ * or unbounded, and bounded queues can suspend, drop, or slide values when * producers are faster than consumers. * + * @stability stable * @since 3.8.0 */ import * as Arr from "./Array.ts" @@ -40,6 +41,7 @@ const DequeueTypeId = "~effect/Queue/Dequeue" * @see {@link isEnqueue} for checking values that only need write access * @see {@link isDequeue} for checking values that only need read access * + * @stability stable * @category guards * @since 2.0.0 */ @@ -64,6 +66,7 @@ export const isQueue = ( * @see {@link isDequeue} for checking for the read side of a queue * @see {@link asEnqueue} for narrowing an existing `Queue` to its write-only interface * + * @stability stable * @category guards * @since 2.0.0 */ @@ -84,6 +87,7 @@ export const isEnqueue = ( * @see {@link isEnqueue} for checking for the write side of a queue * @see {@link asDequeue} for narrowing an existing `Queue` to its read-only interface * + * @stability stable * @category guards * @since 2.0.0 */ @@ -107,6 +111,7 @@ export const isDequeue = ( * @see {@link asDequeue} for exposing only the read side of a `Queue` * @see {@link Enqueue} for the write-only queue handle returned by this conversion * + * @stability stable * @category converting * @since 4.0.0 */ @@ -128,6 +133,7 @@ export const asEnqueue = (self: Queue): Enqueue => self * @see {@link asEnqueue} for narrowing a queue to its producer side * @see {@link Dequeue} for the consumer-side queue handle returned by this function * + * @stability stable * @category converting * @since 4.0.0 */ @@ -162,6 +168,7 @@ export const asDequeue: (self: Queue) => Dequeue = identity * await Effect.runPromise(program) // => ["hello", "world", "!"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -179,6 +186,7 @@ export interface Enqueue extends Inspectable { * Companion namespace containing type-level metadata for the `Enqueue` * write-only queue interface. * + * @stability stable * @since 2.0.0 */ export declare namespace Enqueue { @@ -230,6 +238,7 @@ export declare namespace Enqueue { * await Effect.runPromise(program) // => "a" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -247,6 +256,7 @@ export interface Dequeue extends Inspectable { * Companion namespace containing type-level metadata for the `Dequeue` * read-only queue interface. * + * @stability stable * @since 2.0.0 */ export declare namespace Dequeue { @@ -298,6 +308,7 @@ export declare namespace Dequeue { * await Effect.runPromise(program) // => ["hello", "world", "!"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -309,6 +320,7 @@ export interface Queue extends Enqueue, Dequeu * Companion namespace containing type-level metadata and low-level state types * for `Queue`. * + * @stability stable * @since 2.0.0 */ export declare namespace Queue { @@ -461,6 +473,7 @@ const QueueProto = { * await Effect.runPromise(program) // => { messages: [1, 2, 3, 4, 5], done: Cause.Done(), failed: true } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -513,6 +526,7 @@ export const make = ( * await Effect.runPromise(program) // => 2 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -550,6 +564,7 @@ export const bounded = (capacity: number): Effect> => * await Effect.runPromise(program) // => [2, 3, 4] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -586,6 +601,7 @@ export const sliding = (capacity: number): Effect> => * await Effect.runPromise(program) // => [true, true, false, [1, 2]] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -624,6 +640,7 @@ export const dropping = (capacity: number): Effect> => * await Effect.runPromise(program) // => { size: 5, messages: ["message1", "message2", "message3", "message4", "message5"] } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -658,6 +675,7 @@ export const unbounded = (): Effect> => make() * await Effect.runPromise(program) // => { offered: [true, true], size: 2 } * ``` * + * @stability stable * @category offering * @since 2.0.0 */ @@ -710,6 +728,7 @@ export const offer: { * await Effect.runPromise(program) // => { offered: [true, true], size: 2 } * ``` * + * @stability stable * @category offering * @since 4.0.0 */ @@ -764,6 +783,7 @@ export const offerUnsafe = (self: Enqueue, message: Types.NoInfer * await Effect.runPromise(program) // => [4, 5] * ``` * + * @stability stable * @category offering * @since 2.0.0 */ @@ -814,6 +834,7 @@ export const offerAll: { * await Effect.runPromise(program) // => { remaining: [4, 5], size: 3 } * ``` * + * @stability stable * @category offering * @since 4.0.0 */ @@ -874,6 +895,7 @@ export const offerAllUnsafe = (self: Enqueue, messages: Iterable) * await Effect.runPromise(program) // => [true, Exit.fail("Something went wrong")] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -906,6 +928,7 @@ export const fail: { * await Effect.runPromise(program) // => [true, Exit.failCause(Cause.fail("Queue processing failed"))] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -951,6 +974,7 @@ export const failCause: { * await Effect.runPromise(program) // => [true, Exit.failCause(Cause.fail("Processing error"))] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1011,6 +1035,7 @@ export const failCauseUnsafe = (self: Enqueue, cause: Cause): boo * await Effect.runPromise(program) // => [true, false, 1] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1062,6 +1087,7 @@ export const end = (self: Enqueue): Effect => failCa * await Effect.runPromise(program) // => { ended: true, states: ["Closing", "Done"] } * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1106,6 +1132,7 @@ export const endUnsafe = (self: Enqueue) => failCauseUnsafe(s * await Effect.runPromise(program) // => { interrupted: true, offerResult: false, messages: [1, 2], isDone: true } * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1145,6 +1172,7 @@ export const interrupt = (self: Enqueue): Effect => * ``` * * @see {@link shutdownUnsafe} for synchronous shutdown + * @stability stable * @category completion * @since 2.0.0 */ @@ -1168,6 +1196,7 @@ export const shutdown = (self: Enqueue): Effect => internal * * @see {@link shutdown} for the effectful variant * @see {@link failCauseUnsafe} to set a failure before discarding buffered messages + * @stability stable * @category completion * @since 4.0.0 */ @@ -1217,16 +1246,14 @@ export const shutdownUnsafe = (self: Enqueue): boolean => { * await Effect.runPromise(program) // => { messages: [1, 2, 3, 4, 5], size: 0, empty: [] } * ``` * + * @stability stable * @category taking * @since 4.0.0 */ export const clear = (self: Dequeue): Effect, Pull.ExcludeDone> => - internalEffect.suspend(() => { + suspendTake(() => { if (self.state._tag === "Done") { - if (Pull.isDoneCause(self.state.exit.cause)) { - return internalEffect.succeed([]) - } - return self.state.exit + return Pull.catchDone(self.state.exit, () => internalEffect.succeed([])) } const messages = takeAllUnsafe(self) releaseCapacity(self) @@ -1266,6 +1293,7 @@ export const clear = (self: Dequeue): Effect, Pull.ExcludeD * await Effect.runPromise(program) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category taking * @since 2.0.0 */ @@ -1294,6 +1322,7 @@ export const takeAll = (self: Dequeue): Effect, * await Effect.runPromise(program) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category taking * @since 4.0.0 */ @@ -1354,6 +1383,7 @@ export const collect = (self: Dequeue): Effect, Pull * await Effect.runPromise(program) // => [[1, 2, 3], [4, 5], [6, 7]] * ``` * + * @stability stable * @category taking * @since 2.0.0 */ @@ -1399,6 +1429,7 @@ export const takeN: { * await Effect.runPromise(program) // => [[1, 2, 3, 4, 5], [6, 7, 8]] * ``` * + * @stability stable * @category taking * @since 2.0.0 */ @@ -1408,7 +1439,7 @@ export const takeBetween: { } = dual(3, (self: Dequeue, min: number, max: number): Effect, E> => { min = Count.normalize(min) max = Count.normalize(max) - return internalEffect.suspend(() => + return suspendTake(() => takeBetweenUnsafe(self, min, max) ?? internalEffect.andThen(awaitTake(self, () => canTake(self, min)), takeBetween(self, min, max)) ) @@ -1450,13 +1481,12 @@ export const takeBetween: { * await Effect.runPromise(program) // => [["first", "second"], Exit.fail(Cause.Done())] * ``` * + * @stability stable * @category taking * @since 2.0.0 */ export const take = (self: Dequeue): Effect => - internalEffect.suspend(() => - takeUnsafe(self) ?? internalEffect.andThen(awaitTake(self, () => canTake(self, 1)), take(self)) - ) + suspendTake(() => takeUnsafe(self) ?? internalEffect.andThen(awaitTake(self, () => canTake(self, 1)), take(self))) /** * Attempts to take one item from the queue without waiting. @@ -1489,11 +1519,12 @@ export const take = (self: Dequeue): Effect => * await Effect.runPromise(program) // => [Option.none(), Option.some(42)] * ``` * + * @stability stable * @category taking * @since 2.0.0 */ export const poll = (self: Dequeue): Effect> => - internalEffect.suspend(() => { + suspendTake(() => { const result = takeUnsafe(self) if (result === undefined) { return internalEffect.succeed(Option.none()) @@ -1528,6 +1559,7 @@ export const poll = (self: Dequeue): Effect> => * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category taking * @since 4.0.0 */ @@ -1583,6 +1615,7 @@ export const peek = (self: Dequeue): Effect => * await Effect.runPromise(program) // => [Exit.succeed(1), Exit.succeed(2), undefined] * ``` * + * @stability stable * @category taking * @since 4.0.0 */ @@ -1635,6 +1668,7 @@ export const takeUnsafe = (self: Dequeue): Exit | undefined => * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category offering * @since 4.0.0 */ @@ -1673,6 +1707,7 @@ export const flushUnsafe = (self: Enqueue): void => releaseTakers(se * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category offering * @since 4.0.0 */ @@ -1718,6 +1753,7 @@ export { * @see {@link interrupt} for graceful interruption after buffered messages are drained * @see {@link shutdown} for immediately discarding buffered messages and resuming pending operations * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1761,6 +1797,7 @@ export { * await Effect.runPromise(program) // => [0, 5, 5] * ``` * + * @stability stable * @category sizes * @since 2.0.0 */ @@ -1789,6 +1826,7 @@ export const size = (self: Dequeue): Effect => internalEffec * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1839,6 +1877,7 @@ export const isFull = (self: Dequeue): Effect => internalEf * await Effect.runPromise(program) // => [0, 3, 3] * ``` * + * @stability stable * @category sizes * @since 4.0.0 */ @@ -1872,6 +1911,7 @@ export const sizeUnsafe = (self: Dequeue): number => self.state._tag * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1908,6 +1948,7 @@ export const isFullUnsafe = (self: Dequeue): boolean => sizeUnsafe(s * await Effect.runPromise(program) // => [true, Exit.fail(Cause.Done())] * ``` * + * @stability stable * @category completion * @since 4.0.0 */ @@ -1989,6 +2030,15 @@ const takeBetweenUnsafe = ( return core.exitSucceed(messages) } +// Deliver dequeued results in the same fiber step to prevent message loss on interruption. +const suspendTake: (f: () => Effect) => Effect = core.makePrimitive({ + op: "QueueTake", + [core.evaluate](fiber) { + const effect = this[core.args]() + return core.isExit(effect) ? (effect as unknown as core.Primitive)[core.evaluate](fiber) : effect + } +}) + // Whether a take of at least `min` messages can complete without waiting. // A closing queue receives no more messages, so any remainder satisfies `min`. const canTake = (self: Dequeue, min: number): boolean => @@ -2078,6 +2128,11 @@ const releaseCapacity = (self: Dequeue): boolean => { self.state.offers.delete(entry) entry.resume(core.exitSucceed([])) } + // The resumed producer may have replaced the state with Done. + const state = self.state as Queue.State + if (state._tag === "Done") { + return Pull.isDoneCause(state.exit.cause) + } } return false } diff --git a/repos/effect/packages/effect/src/Random.ts b/packages/effect/src/Random.ts similarity index 98% rename from repos/effect/packages/effect/src/Random.ts rename to packages/effect/src/Random.ts index 67b2c5781..e2fc7b61a 100644 --- a/repos/effect/packages/effect/src/Random.ts +++ b/packages/effect/src/Random.ts @@ -6,6 +6,7 @@ * random generation is a service, tests and applications can replace the * generator used by Effect programs. * + * @stability stable * @since 4.0.0 */ import type * as Arr from "./Array.ts" @@ -20,6 +21,7 @@ import * as Predicate from "./Predicate.ts" /** * The service used to generate pseudo-random numbers. * + * @stability stable * @category services * @since 4.0.0 */ @@ -63,6 +65,7 @@ export interface Random { * await Effect.runPromise(program.pipe(Random.withSeed("example"))) // => [0.1633802591287037, 3434461687501127, 1] * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -87,6 +90,7 @@ const randomWith = (f: (random: Random) => A): Effect.Effect => * await Effect.runPromise(Random.next.pipe(Random.withSeed("example"))) // => 0.1633802591287037 * ``` * + * @stability stable * @category generators * @since 2.0.0 */ @@ -107,6 +111,7 @@ export const next: Effect.Effect = randomWith((r) => r.nextDoubleUnsafe( * await Effect.runPromise(Random.nextBoolean.pipe(Random.withSeed("example"))) // => false * ``` * + * @stability stable * @category generators * @since 2.0.0 */ @@ -129,6 +134,7 @@ export const nextBoolean: Effect.Effect = randomWith((r) => r.nextDoubl * await Effect.runPromise(Random.nextInt.pipe(Random.withSeed("example"))) // => -6064002158214091 * ``` * + * @stability stable * @category generators * @since 2.0.0 */ @@ -149,6 +155,7 @@ export const nextInt: Effect.Effect = randomWith((r) => r.nextIntUnsafe( * await Effect.runPromise(Random.nextBetween(0, 1).pipe(Random.withSeed("example"))) // => 0.1633802591287037 * ``` * + * @stability stable * @category generators * @since 4.0.0 */ @@ -185,6 +192,7 @@ export const nextBetween = (min: number, max: number): Effect.Effect => * await Effect.runPromise(program.pipe(Random.withSeed("example"))) // => [1, 4, 0] * ``` * + * @stability stable * @category generators * @since 2.0.0 */ @@ -214,6 +222,7 @@ export const nextIntBetween = (min: number, max: number, options?: { * await Effect.runPromise(Random.shuffle([1, 2, 3, 4, 5]).pipe(Random.withSeed("example"))) // => [4, 2, 5, 3, 1] * ``` * + * @stability stable * @category generators * @since 2.0.0 */ @@ -250,6 +259,7 @@ export const shuffle = (elements: Iterable): Effect.Effect> => * await Effect.runPromise(Random.choice(["red", "green", "blue"] as const).pipe(Random.withSeed("example"))) // => "red" * ``` * + * @stability stable * @category generators * @since 3.6.0 */ @@ -298,6 +308,7 @@ export const choice: >( * ])) // => [[0.018368576514773527, 0.4010840628128671], [0.018368576514773527, 0.4010840628128671]] * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/RcMap.ts b/packages/effect/src/RcMap.ts similarity index 99% rename from repos/effect/packages/effect/src/RcMap.ts rename to packages/effect/src/RcMap.ts index fbb97f76a..d3c5b6cb7 100644 --- a/repos/effect/packages/effect/src/RcMap.ts +++ b/packages/effect/src/RcMap.ts @@ -9,6 +9,7 @@ * lifecycles such as clients, sessions, and connections, not as a general * mutable cache. * + * @stability stable * @since 3.5.0 */ import * as Cause from "./Cause.ts" @@ -67,6 +68,7 @@ const TypeId = "~effect/RcMap" * @see {@link make} for creating an `RcMap` * @see {@link get} for acquiring or retaining a resource by key * + * @stability stable * @category models * @since 3.5.0 */ @@ -93,6 +95,7 @@ export interface RcMap extends Pipeable { * @see {@link State.Open} for the active state with entries * @see {@link State.Closed} for the shutdown state * + * @stability stable * @category models * @since 4.0.0 */ @@ -106,6 +109,7 @@ export type State = State.Open | State.Closed * Use when referring to the concrete open, closed, and entry state shapes used * by `RcMap`. * + * @stability stable * @since 4.0.0 */ export declare namespace State { @@ -234,6 +238,7 @@ const makeUnsafe = (options: { * @see {@link get} for acquiring or retaining a resource by key * @see {@link invalidate} for removing a resource from the map * + * @stability stable * @category constructors * @since 3.5.0 */ @@ -329,6 +334,7 @@ export const make: { * @see {@link make} for creating the reference-counted map * @see {@link invalidate} for removing a resource by key * + * @stability stable * @category combinators * @since 3.5.0 */ @@ -429,6 +435,7 @@ export const get: { * @see {@link get} for acquiring a resource when the key is missing * @see {@link has} for checking presence without retaining or awaiting the entry * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -558,6 +565,7 @@ const release = (self: RcMap, key: K, entry: State.Entry * * @see {@link has} for checking one key without enumerating all keys * + * @stability stable * @category combinators * @since 3.8.0 */ @@ -609,6 +617,7 @@ export const keys = (self: RcMap): Effect.Effect> * @see {@link get} for acquiring or retaining the resource for a key * @see {@link touch} for extending the idle lifetime without removing the entry * + * @stability stable * @category combinators * @since 3.13.0 */ @@ -649,6 +658,7 @@ export const invalidate: { * @see {@link get} for acquiring or retaining the resource for a key * @see {@link keys} for enumerating all currently stored keys * + * @stability stable * @category combinators * @since 3.17.7 */ @@ -707,6 +717,7 @@ export const has: { * * @see {@link invalidate} for removing the resource instead of extending it * + * @stability stable * @category combinators * @since 3.13.0 */ diff --git a/repos/effect/packages/effect/src/RcRef.ts b/packages/effect/src/RcRef.ts similarity index 98% rename from repos/effect/packages/effect/src/RcRef.ts rename to packages/effect/src/RcRef.ts index 8c81fce74..d2e4c3eaa 100644 --- a/repos/effect/packages/effect/src/RcRef.ts +++ b/packages/effect/src/RcRef.ts @@ -6,6 +6,7 @@ * available. The module also provides `invalidate` for forcing the next `get` * to acquire a fresh resource. * + * @stability stable * @since 3.5.0 */ import type * as Duration from "./Duration.ts" @@ -61,6 +62,7 @@ const TypeId = "~effect/RcRef" * await Effect.runPromise(Effect.scoped(program)) // => [true, ["closed Connected to postgres://localhost"]] * ``` * + * @stability stable * @category models * @since 3.5.0 */ @@ -82,6 +84,7 @@ export interface RcRef extends Pipeable { * * ``` * + * @stability stable * @since 3.5.0 */ export declare namespace RcRef { @@ -151,6 +154,7 @@ export declare namespace RcRef { * events // => ["released foo"] * ``` * + * @stability stable * @category constructors * @since 3.5.0 */ @@ -214,6 +218,7 @@ export const make: ( * await Effect.runPromise(Effect.scoped(program)) // => [true, ["released shared resource"]] * ``` * + * @stability stable * @category combinators * @since 3.5.0 */ @@ -238,6 +243,7 @@ export const get: (self: RcRef) => Effect.Effect = inte * * @see {@link get} for acquiring the current cached resource or the fresh resource after invalidation * + * @stability stable * @category combinators * @since 3.19.6 */ diff --git a/repos/effect/packages/effect/src/Record.ts b/packages/effect/src/Record.ts similarity index 97% rename from repos/effect/packages/effect/src/Record.ts rename to packages/effect/src/Record.ts index 198c3fb5e..74c9af49f 100644 --- a/repos/effect/packages/effect/src/Record.ts +++ b/packages/effect/src/Record.ts @@ -7,6 +7,7 @@ * entries. Helpers that change values return new records instead of mutating the * input. * + * @stability stable * @since 2.0.0 */ @@ -41,6 +42,7 @@ import type { NoInfer } from "./Types.ts" * user // => { name: "John", age: 30 } * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -67,6 +69,7 @@ export type ReadonlyRecord = { * "b" satisfies CommonKeys * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace ReadonlyRecord { @@ -167,6 +170,7 @@ export declare namespace ReadonlyRecord { * defaults // => { port: 3000, retries: 3 } * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -190,6 +194,7 @@ export interface ReadonlyRecordTypeLambda extends Typ * Record.set(emptyRecord, "count", 42) // => { count: 42 } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -210,6 +215,7 @@ export const empty = (): Record< * Record.isEmptyRecord({ a: 3 }) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -228,6 +234,7 @@ export const isEmptyRecord = (self: Record): self is * Record.isEmptyReadonlyRecord({ a: 3 }) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -247,6 +254,7 @@ export const isEmptyReadonlyRecord: ( * Record.fromIterableWith([1, 2, 3, 4], (a) => [String(a), a * 2]) // => { "1": 2, "2": 4, "3": 6, "4": 8 } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -292,6 +300,7 @@ export const fromIterableWith: { * ) // => { "1": { id: "1", name: "name1" }, "2": { id: "2", name: "name2" } } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -327,6 +336,7 @@ export const fromIterableBy: { * Record.fromEntries([["a", 1], ["b", 2]]) // => { a: 1, b: 2 } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -346,6 +356,7 @@ export const fromEntries: ( * Record.collect(x, (key, n) => [key, n]) // => [["a", 1], ["b", 2], ["c", 3]] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -375,6 +386,7 @@ export const collect: { * Record.toEntries(x) // => [["a", 1], ["b", 2], ["c", 3]] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -394,6 +406,7 @@ export const toEntries: (self: ReadonlyRecord) => Arr * Record.size({ a: "a", b: 1, c: true }) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -411,6 +424,7 @@ export const size = (self: ReadonlyRecord): number => * Record.has(Record.empty(), "c") // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -444,6 +458,7 @@ export const has: { * R.get(person, "email") // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -473,6 +488,7 @@ export const get: { * Record.modify(input, "b", f) // => Option.none() * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -516,6 +532,7 @@ export const modify: { * Record.replace(Record.empty(), "a", 10) // => Option.none() * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -558,6 +575,7 @@ export const replace: { * Record.remove({ a: 1, b: 2 }, "a") // => { b: 2 } * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -592,6 +610,7 @@ export const remove: { * Record.pop(input, "c") // => Option.none() * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -626,6 +645,7 @@ export const pop: { * Record.map({ a: 3, b: 5 }, g) // => { a: "A-3", b: "B-5" } * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -654,6 +674,7 @@ export const map: { * Record.mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()) // => { A: 3, B: 5 } * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -691,6 +712,7 @@ export const mapKeys: { * Record.mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]) // => { A: 4, B: 6 } * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -731,6 +753,7 @@ export const mapEntries: { * Record.filterMap(x, f) // => { c: 6 } * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -771,6 +794,7 @@ export const filterMap: { * Record.filter(x, (n) => n > 2) // => { c: 3, d: 4 } * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -817,6 +841,7 @@ export const filter: { * Record.getSomes({ a: Option.some(1), b: Option.none(), c: Option.some(2) }) // => { a: 1, c: 2 } * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -851,6 +876,7 @@ export const getSomes: ( * }) // => { b: "err" } * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -884,6 +910,7 @@ export const getFailures = ( * }) // => { a: 1, c: 2 } * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -920,6 +947,7 @@ export const getSuccesses = ( * Record.partition(x, f) // => [{ b: 2 }, { a: 1, c: 3 }] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -965,6 +993,7 @@ export const partition: { * Record.separate({ a: Result.fail("e"), b: Result.succeed(1) }) // => [{ b: 1 }, { a: "e" }] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -983,6 +1012,7 @@ export const separate: ( * Record.keys({ a: 1, b: 2, c: 3 }) // => ["a", "b", "c"] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1000,6 +1030,7 @@ export const keys = (self: ReadonlyRecord): * Record.values({ a: 1, b: 2, c: 3 }) // => [1, 2, 3] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1017,6 +1048,7 @@ export const values = (self: ReadonlyRecord): Array { a: 1, b: 2, c: 5 } * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -1073,6 +1105,7 @@ export const set: { * ``` * * @see {@link set} for an immutable update + * @stability stable * @category mutations * @since 4.0.0 */ @@ -1102,6 +1135,7 @@ export const assignProperty: (self: object, key: PropertyKey, value: unknown) => * isSubrecord(required, { role: "editor", status: "active" }) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1135,6 +1169,7 @@ export const isSubrecordBy = (equivalence: Equivalence): { * Record.isSubrecord({ a: 1, b: 2 }, { a: 1 } as Record) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1154,6 +1189,7 @@ export const isSubrecord: { * Record.reduce({ a: 1, b: 2, c: 3 }, 0, (acc, value) => acc + value) // => 6 * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -1190,6 +1226,7 @@ export const reduce: { * Record.every({ a: 1, b: -1 }, (n) => n > 0) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -1230,6 +1267,7 @@ export const every: { * Record.some({ a: 1, b: 2 }, (n) => n > 2) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -1260,6 +1298,7 @@ export const some: { * Record.union({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b) // => { a: 1, b: 5, c: 4 } * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1315,6 +1354,7 @@ export const union: { * Record.intersection({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b) // => { b: 5 } * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1360,6 +1400,7 @@ export const intersection: { * Record.difference({ a: 1, b: 2 }, { b: 3, c: 4 }) // => { a: 1, c: 4 } * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -1410,6 +1451,7 @@ export const difference: { * recordEquivalence({ a: 1, b: 2 }, { a: 1, b: 3 }) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -1431,6 +1473,7 @@ export const makeEquivalence = ( * Record.singleton("a", 1) // => { a: 1 } * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1457,6 +1500,7 @@ export const singleton = (key: K, value: A): Recor * @see {@link union} for one-off record merging with the same union semantics * @see {@link makeReducerIntersection} for a reducer that keeps only keys present on both sides * + * @stability stable * @category combining * @since 4.0.0 */ @@ -1485,6 +1529,7 @@ export function makeReducerUnion(combiner: Combiner.Combine * @see {@link makeReducerUnion} for a reducer that preserves keys from either input record * @see {@link intersection} for applying the shared-key merge to one pair of records * + * @stability stable * @category combining * @since 4.0.0 */ @@ -1513,6 +1558,7 @@ export function makeReducerIntersection( * ) // => Option.some(["c", 3]) * ``` * + * @stability stable * @category searching * @since 3.14.0 */ diff --git a/repos/effect/packages/effect/src/Redactable.ts b/packages/effect/src/Redactable.ts similarity index 97% rename from repos/effect/packages/effect/src/Redactable.ts rename to packages/effect/src/Redactable.ts index b02eb30f2..e0b234796 100644 --- a/repos/effect/packages/effect/src/Redactable.ts +++ b/packages/effect/src/Redactable.ts @@ -6,6 +6,7 @@ * Typical use cases include masking secrets, tokens, or personal data in logs, traces, * and serialized output. * + * @stability stable * @since 4.0.0 */ import type * as Context from "./Context.ts" @@ -45,6 +46,7 @@ import { hasProperty } from "./Predicate.ts" * * @see {@link Redactable} for the interface this symbol belongs to * @see {@link isRedactable} to check whether a value has this symbol + * @stability stable * @category symbols * @since 3.10.0 */ @@ -82,6 +84,7 @@ export const symbolRedactable: unique symbol = Symbol.for("~effect/Redactable") * @see {@link symbolRedactable} for the symbol key to implement * @see {@link redact} to apply redaction to any value * @see {@link isRedactable} for the type guard for this interface + * @stability stable * @category models * @since 3.10.0 */ @@ -99,6 +102,7 @@ export interface Redactable { * * @see {@link Redactable} for the interface being checked * @see {@link redact} to apply redaction if the value is redactable + * @stability stable * @category guards * @since 3.10.0 */ @@ -125,6 +129,7 @@ export const isRedactable = (u: unknown): u is Redactable => hasProperty(u, symb * * @see {@link isRedactable} to check before redacting * @see {@link getRedacted} for the lower-level variant for known redactables + * @stability stable * @category destructors * @since 3.10.0 */ @@ -153,6 +158,7 @@ export function redact(u: unknown): unknown { * * @see {@link redact} for the higher-level variant that handles non-redactable values * @see {@link isRedactable} for the type guard to verify before calling this + * @stability stable * @category destructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Redacted.ts b/packages/effect/src/Redacted.ts similarity index 98% rename from repos/effect/packages/effect/src/Redacted.ts rename to packages/effect/src/Redacted.ts index be67a9e65..e14d731e2 100644 --- a/repos/effect/packages/effect/src/Redacted.ts +++ b/packages/effect/src/Redacted.ts @@ -8,6 +8,7 @@ * values, and comparison helpers that avoid exposing the wrapped value at the * call site. * + * @stability stable * @since 3.3.0 */ import * as Equal from "./Equal.ts" @@ -50,6 +51,7 @@ const TypeId = "~effect/Redacted" * Array.of(String(apiKey), String(userPassword)) // => ["", ""] * ``` * + * @stability stable * @category models * @since 3.3.0 */ @@ -76,6 +78,7 @@ export interface Redacted extends Redacted.Variance, Equal.Eq * Redacted.isRedacted(secret) // => true * ``` * + * @stability stable * @since 3.3.0 */ export declare namespace Redacted { @@ -153,6 +156,7 @@ export declare namespace Redacted { * Redacted.isRedacted(plainString) // => false * ``` * + * @stability stable * @category guards * @since 3.3.0 */ @@ -181,6 +185,7 @@ export const isRedacted = (u: unknown): u is Redacted => hasProperty(u, * String(API_KEY) // => "" * ``` * + * @stability stable * @category constructors * @since 3.3.0 */ @@ -239,6 +244,7 @@ const Proto = { * Redacted.value(API_KEY) // => "1234567890" * ``` * + * @stability stable * @category getters * @since 3.3.0 */ @@ -277,6 +283,7 @@ export const value: (self: Redacted) => T = redacted.value * failure // => Result.fail("Unable to get redacted value") * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -307,6 +314,7 @@ export const wipeUnsafe = (self: Redacted): boolean => redacted.redactedRe * equivalence(API_KEY1, API_KEY3) // => true * ``` * + * @stability stable * @category instances * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Reducer.ts b/packages/effect/src/Reducer.ts similarity index 98% rename from repos/effect/packages/effect/src/Reducer.ts rename to packages/effect/src/Reducer.ts index 6f1a6e42b..8371399af 100644 --- a/repos/effect/packages/effect/src/Reducer.ts +++ b/packages/effect/src/Reducer.ts @@ -5,6 +5,7 @@ * constructor for reducers and a helper for reversing the order in which values * are combined. * + * @stability stable * @since 4.0.0 */ @@ -48,6 +49,7 @@ import type * as Combiner from "./Combiner.ts" * @see {@link make} – create a `Reducer` from a function and initial value * @see {@link Combiner.Combiner} – parent interface without `initialValue` * + * @stability stable * @category models * @since 4.0.0 */ @@ -111,6 +113,7 @@ export interface Reducer extends Combiner.Combiner { * @see {@link Reducer} – the interface this creates * @see {@link flip} – reverse the argument order * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -163,6 +166,7 @@ export function make( * @see {@link make} * @see {@link Combiner.flip} – the same operation on a plain `Combiner` * + * @stability stable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Ref.ts b/packages/effect/src/Ref.ts similarity index 98% rename from repos/effect/packages/effect/src/Ref.ts rename to packages/effect/src/Ref.ts index a5c783fca..c1663d44a 100644 --- a/repos/effect/packages/effect/src/Ref.ts +++ b/packages/effect/src/Ref.ts @@ -8,6 +8,7 @@ * update variants that leave the value unchanged when an `Option.none` result * is returned. * + * @stability stable * @since 2.0.0 */ import * as Effect from "./Effect.ts" @@ -53,6 +54,7 @@ const TypeId = "~effect/Ref" * @see {@link get} for reading the current value * @see {@link set} for replacing the current value * + * @stability stable * @category models * @since 2.0.0 */ @@ -67,6 +69,7 @@ export interface Ref extends Ref.Variance, Pipeable { * * Use when referring to type members nested under the `Ref` namespace. * + * @stability stable * @since 2.0.0 */ export declare namespace Ref { @@ -136,6 +139,7 @@ const RefProto = { * Ref.getUnsafe(counter) // => 0 * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -167,6 +171,7 @@ export const makeUnsafe = (value: A): Ref => { * * @see {@link makeUnsafe} for synchronous construction outside Effect code * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -194,6 +199,7 @@ export const make = (value: A): Effect.Effect> => Effect.sync(() => ma * * @see {@link set} for replacing the current value * + * @stability stable * @category getters * @since 2.0.0 */ @@ -230,6 +236,7 @@ export const get = (self: Ref) => Effect.sync(() => self.ref.current) * @see {@link getAndSet} for setting while returning the previous value * @see {@link setAndGet} for setting while returning the new value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -264,6 +271,7 @@ export const set = dual< * @see {@link set} for setting without returning the previous value * @see {@link getAndUpdate} for deriving the new value from the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -303,6 +311,7 @@ export const getAndSet = dual< * @see {@link update} for updating without returning the previous value * @see {@link updateAndGet} for returning the new value instead * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -356,6 +365,7 @@ export const getAndUpdate = dual< * @see {@link getAndUpdate} for always applying an update * @see {@link updateSome} for conditional updates without returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -402,6 +412,7 @@ export const getAndUpdateSome = dual< * await Effect.runPromise(program2) // => 20 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -455,6 +466,7 @@ export const setAndGet = dual< * @see {@link updateAndGet} for returning the new stored value * @see {@link modifySome} for optionally updating while returning a separate result * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -515,6 +527,7 @@ export const modify = dual< * @see {@link modify} for always storing a new value * @see {@link updateSome} for optional updates without a separate return value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -567,6 +580,7 @@ export const modifySome: { * @see {@link updateAndGet} for returning the new value * @see {@link getAndUpdate} for returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -604,6 +618,7 @@ export const update = dual< * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for returning the previous value instead * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -652,6 +667,7 @@ export const updateAndGet = dual< * @see {@link update} for always applying an update * @see {@link updateSomeAndGet} for returning the resulting current value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -705,6 +721,7 @@ export const updateSome = dual< * @see {@link updateSome} for conditional updates without returning a value * @see {@link updateAndGet} for always updating and returning the new value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -741,6 +758,7 @@ export const updateSomeAndGet = dual< * Ref.getUnsafe(counter) // => 42 * ``` * + * @stability stable * @category getters * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/References.ts b/packages/effect/src/References.ts similarity index 97% rename from repos/effect/packages/effect/src/References.ts rename to packages/effect/src/References.ts index 5d5e51dd9..95b479181 100644 --- a/repos/effect/packages/effect/src/References.ts +++ b/packages/effect/src/References.ts @@ -9,6 +9,7 @@ * providing a new value changes behavior for the provided effect and the fibers * it starts. * + * @stability stable * @since 4.0.0 */ import type * as Context from "./Context.ts" @@ -31,6 +32,7 @@ export { * * @see {@link MinimumTraceLevel} for configuring the threshold that decides whether spans at a given level are sampled or exported * + * @stability stable * @category references * @since 4.0.0 */ @@ -50,6 +52,7 @@ export { * * @see {@link TracerEnabled} for disabling span registration instead of only propagation * + * @stability stable * @category references * @since 4.0.0 */ @@ -68,6 +71,7 @@ export { * * @see {@link PreventSchedulerYield} for bypassing scheduler yield checks instead of changing the operation budget * + * @stability stable * @category references * @since 4.0.0 */ @@ -82,6 +86,7 @@ export { * * @see {@link CurrentTraceLevel} for setting the level assigned to spans before this threshold is applied * + * @stability stable * @category references * @since 4.0.0 */ @@ -106,6 +111,7 @@ export { * * @see {@link MaxOpsBeforeYield} for tuning the operation budget while keeping scheduler yield checks enabled * + * @stability stable * @category references * @since 4.0.0 */ @@ -118,6 +124,7 @@ export { * Use to access or override the active tracer service through the references * module when working directly with Effect runtime references. * + * @stability stable * @category references * @since 4.0.0 */ @@ -179,6 +186,7 @@ export { * await Effect.runPromise(logAnnotationExample) // => [0, ["req-123", "user-456", "1.0.0"], ["data-sync", 1234567890]] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -219,6 +227,7 @@ export const CurrentLogAnnotations: Context.Reference ["Info", "Error"] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -284,6 +293,7 @@ export const CurrentLogLevel: Context.Reference = references.CurrentLo * await Effect.runPromise(logSpanExample) // => [0, ["database-connection"], ["database-connection", "data-processing"], 0] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -307,6 +317,7 @@ export const CurrentLogSpans: Context.Reference = refe * levels // => ["Warn", "Error"] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -385,6 +397,7 @@ export const MinimumLogLevel: Context.Reference = references.MinimumLo * await Effect.runPromise(tracingControl) // => [true, false, true] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -439,6 +452,7 @@ export const TracerEnabled: Context.Reference = references.TracerEnable * await Effect.runPromise(spanAnnotationExample) // => [0, ["user-service", "1.2.3", "production"], 0] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -498,6 +512,7 @@ export const TracerSpanAnnotations: Context.Reference [0, 1, 0] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -541,6 +556,7 @@ export const TracerSpanLinks: Context.Reference> = refer * await Effect.runPromise(tracingControl) // => [true, false, true] * ``` * + * @stability stable * @category references * @since 4.0.0 */ @@ -567,6 +583,7 @@ export const TracerTimingEnabled: Context.Reference = references.Tracer * @see {@link CurrentLogLevel} for the default severity used by ordinary `Effect.log` calls * @see {@link MinimumLogLevel} for filtering emitted log entries by threshold * + * @stability stable * @category references * @since 4.0.0 */ @@ -587,6 +604,7 @@ export const UnhandledLogLevel: Context.Reference = refere * * @see {@link CurrentStackFrame} for the fiber reference carrying the active stack-frame chain * + * @stability stable * @category references * @since 4.0.0 */ @@ -611,6 +629,7 @@ export interface StackFrame { * Providing this reference changes which `Logger` instances receive log entries * in the current context. * + * @stability stable * @category references * @since 4.0.0 */ @@ -629,6 +648,7 @@ export const CurrentLoggers: Context.Reference> * The default value is `false`. When set to `true`, the built-in default logger * and TTY pretty console logger call `console.error` instead of `console.log`. * + * @stability stable * @category references * @since 4.0.0 */ @@ -667,6 +687,7 @@ export { * await Effect.runPromise(customScheduling) // => [true, true] * ``` * + * @stability stable * @category references * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/RegExp.ts b/packages/effect/src/RegExp.ts similarity index 96% rename from repos/effect/packages/effect/src/RegExp.ts rename to packages/effect/src/RegExp.ts index c1c97ea3e..369d19d8b 100644 --- a/repos/effect/packages/effect/src/RegExp.ts +++ b/packages/effect/src/RegExp.ts @@ -8,6 +8,7 @@ * data-driven text, check whether an unknown value is already a `RegExp`, or * access the native constructor without leaving the Effect namespace. * + * @stability stable * @since 2.0.0 */ import * as predicate from "./Predicate.ts" @@ -31,6 +32,7 @@ import * as predicate from "./Predicate.ts" * pattern.test("goodbye") // => false * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -52,6 +54,7 @@ export const RegExp = globalThis.RegExp * RegExp.isRegExp("a") // => false * ``` * + * @stability stable * @category guards * @since 3.9.0 */ @@ -72,6 +75,7 @@ export const isRegExp: (input: unknown) => input is RegExp = predicate.isRegExp * RegExp.escape("a*b") // => "a\\*b" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Request.ts b/packages/effect/src/Request.ts similarity index 97% rename from repos/effect/packages/effect/src/Request.ts rename to packages/effect/src/Request.ts index 5ccb1032e..4bb5fc8f7 100644 --- a/repos/effect/packages/effect/src/Request.ts +++ b/packages/effect/src/Request.ts @@ -7,6 +7,7 @@ * `RequestResolver`, which performs backend-specific loading and completes each * pending request entry with a success, failure, cause, exit, or effect. * + * @stability stable * @since 2.0.0 */ import type * as Cause from "./Cause.ts" @@ -45,6 +46,7 @@ const TypeId = "~effect/Request" * * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -65,6 +67,7 @@ export interface Request extends Variance * This marker preserves the success, error, and service requirement types for * Effect's type-level machinery. Users normally get it by extending `Request`. * + * @stability stable * @category models * @since 2.0.0 */ @@ -116,6 +120,7 @@ export interface Variance { * request.id // => 123 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -140,6 +145,7 @@ export interface Constructor, T extends keyof R * * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -163,6 +169,7 @@ export type Error> = [T] extends [Request> = [T] extends [Request> = [T] extends [Request = { * Request.isRequest("not a request") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -287,6 +298,7 @@ export const isRequest = (u: unknown): u is Request = * request.includeSettings // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -331,6 +343,7 @@ export const of = >(): Constructor => (args) * Array.of(userRequest._tag, postRequest._tag) // => ["GetUser", "GetPost"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -364,6 +377,7 @@ export const tagged = & { _tag: string }>( * getUserRequest.id // => 123 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -403,6 +417,7 @@ export const Class: new, Success, Error = never, C * request.id // => 123 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -430,6 +445,7 @@ export const TaggedClass = ( * @see {@link fail} for completing an entry with a typed failure * @see {@link failCause} for completing an entry with a failure `Cause` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -464,6 +480,7 @@ export const complete: { * @see {@link fail} for completing an entry with a typed failure * @see {@link failCause} for completing an entry with a failure `Cause` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -492,6 +509,7 @@ export const completeEffect: { * @see {@link completeEffect} for completing an entry from an effect result * @see {@link succeed} for completing an entry successfully * + * @stability stable * @category completion * @since 2.0.0 */ @@ -516,6 +534,7 @@ export const fail: { * @see {@link completeEffect} for completing an entry from an effect result * @see {@link succeed} for completing an entry successfully * + * @stability stable * @category completion * @since 2.0.0 */ @@ -541,6 +560,7 @@ export const failCause: { * @see {@link fail} for completing an entry with a typed failure * @see {@link failCause} for completing an entry with a failure `Cause` * + * @stability stable * @category completion * @since 2.0.0 */ @@ -562,6 +582,7 @@ export const succeed: { * an `uninterruptible` flag used by batching and caching internals, and the * `completeUnsafe` callback used by resolvers to supply the final `Exit`. * + * @stability stable * @category models * @since 2.0.0 */ @@ -588,6 +609,7 @@ export interface Entry { * most application code receives entries from a `RequestResolver` instead of * constructing them directly. * + * @stability stable * @category constructors * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/RequestResolver.ts b/packages/effect/src/RequestResolver.ts similarity index 97% rename from repos/effect/packages/effect/src/RequestResolver.ts rename to packages/effect/src/RequestResolver.ts index 02239e675..5df4843a8 100644 --- a/repos/effect/packages/effect/src/RequestResolver.ts +++ b/packages/effect/src/RequestResolver.ts @@ -7,6 +7,7 @@ * resolver shapes and tools for controlling batching, grouping, delays, * tracing, caching, racing, hooks around resolver execution, and persistence. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyArray } from "./Array.ts" @@ -75,6 +76,7 @@ const TypeId = "~effect/RequestResolver" * await Effect.runPromise(program) // => "User 1" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -108,6 +110,7 @@ export interface RequestResolver extends RequestResolv /** * Namespace containing type-level helpers associated with `RequestResolver`. * + * @stability stable * @since 2.0.0 */ export declare namespace RequestResolver { @@ -149,6 +152,7 @@ const RequestResolverProto = { * * @see {@link RequestResolver} for the type narrowed by this guard * + * @stability stable * @category guards * @since 2.0.0 */ @@ -179,6 +183,7 @@ export const isRequestResolver = (u: unknown): u is RequestResolver => hasP * @see {@link make} for constructing a resolver from a batch runner * @see {@link makeGrouped} for constructing a resolver that groups requests by key * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -231,6 +236,7 @@ const defaultKey = (_request: unknown): unknown => defaultKeyObject * await Effect.runPromise(getUserEffect) // => "User 123" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -289,6 +295,7 @@ export const make = ( * result // => ["User 1 with role admin", "User 2 with role admin"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -343,6 +350,7 @@ const hashGroupKey = (get: (entry: Request.Entry) => K) => { * await Effect.runPromise(getSquareEffect) // => 25 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -388,6 +396,7 @@ export const fromFunction = ( * await Effect.runPromise(batchedEffect) // => [2, 4, 6] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -434,6 +443,7 @@ export const fromFunctionBatched = ( * await Effect.runPromise(getUserEffect) // => "User 123 from API" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -500,6 +510,7 @@ export const fromEffect = ( * await Effect.runPromise(program) // => ["User 1", "Post 2"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -584,6 +595,7 @@ export const fromEffectTagged = [true, true] * ``` * + * @stability stable * @category delays & timeouts * @since 4.0.0 */ @@ -627,6 +639,7 @@ export const setDelayEffect: { * await Effect.runPromise(program) // => "data" * ``` * + * @stability stable * @category delays & timeouts * @since 4.0.0 */ @@ -686,6 +699,7 @@ export const setDelay: { * result // => "data" * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -729,6 +743,7 @@ export const around: { * * @see {@link make} for constructing a resolver that executes batches and completes request entries * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -784,6 +799,7 @@ export const never: RequestResolver = make(() => Effect.never) * Array.of(result[0], result[11]) // => ["data-0", "data-11"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -855,6 +871,7 @@ export const batchN: { * result // => ["User 1", "User 2", "User 3"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -916,6 +933,7 @@ export const grouped: { * await Effect.runPromise(program) // => "fast-1" * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -975,6 +993,7 @@ export const race: { * await Effect.runPromise(effect) // => "data-123" * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1041,6 +1060,7 @@ export const withSpan: { * @see {@link persisted} for storing persistable request results outside process memory * @see {@link Cache.Cache} for operations available on the returned cache * + * @stability stable * @category caching * @since 4.0.0 */ @@ -1102,6 +1122,12 @@ export const asCache: { requireServicesAt: options.requireServicesAt ?? "lookup" as ServiceMode }) as any) +interface CacheEntry { + readonly entry: Request.Entry + exit: Request.Result | undefined + pending: Array> +} + /** * Adds a bounded in-memory cache to a request resolver. * @@ -1126,6 +1152,7 @@ export const asCache: { * @see {@link asCache} for exposing the resolver as a `Cache` with time-to-live and service lookup controls * @see {@link persisted} for backing persistable requests with the configured persistence store * + * @stability stable * @category caching * @since 4.0.0 */ @@ -1144,10 +1171,7 @@ export const withCache: { }): Effect.Effect> => Effect.sync(() => { const strategy = options.strategy ?? "lru" - const cache = MutableHashMap.empty - exit: Request.Result | undefined - }>() + const cache = MutableHashMap.empty>() return makeWith({ ...self, runAll(entries, key) { @@ -1165,7 +1189,7 @@ export const withCache: { preCheck(entry) { const ocached = MutableHashMap.get(cache, entry.request) if (ocached._tag === "None") { - const cached = { entry, exit: undefined as Request.Result | undefined } + const cached: CacheEntry = { entry, exit: undefined, pending: [] } MutableHashMap.set(cache, entry.request, cached) const prevComplete = entry.completeUnsafe entry.completeUnsafe = function(exit) { @@ -1179,7 +1203,12 @@ export const withCache: { } else { cached.exit = exit as any } + const pending = cached.pending + cached.pending = [] prevComplete(exit) + for (const pendingEntry of pending) { + pendingEntry.completeUnsafe(exit) + } } return true } @@ -1192,11 +1221,7 @@ export const withCache: { entry.completeUnsafe(cached.exit as any) } else { cached.entry.uninterruptible = true - const prevComplete = cached.entry.completeUnsafe - cached.entry.completeUnsafe = function(exit) { - prevComplete(exit) - entry.completeUnsafe(exit) - } + cached.pending.push(entry) } return false } @@ -1223,6 +1248,7 @@ export const withCache: { * @see {@link withCache} for in-memory resolver caching that does not require persistable request values or a persistence store * @see {@link asCache} for exposing resolver results through a `Cache` instead of returning another resolver * + * @stability unstable * @category caching * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Resource.ts b/packages/effect/src/Resource.ts similarity index 97% rename from repos/effect/packages/effect/src/Resource.ts rename to packages/effect/src/Resource.ts index 7e6d5383e..ef03a2eb9 100644 --- a/repos/effect/packages/effect/src/Resource.ts +++ b/packages/effect/src/Resource.ts @@ -6,6 +6,7 @@ * schedule. Resource acquisition runs in a scope, so replacements and final * cleanup release the resources owned by previous values. * + * @stability stable * @since 2.0.0 */ import * as Context from "./Context.ts" @@ -35,6 +36,7 @@ const TypeId = "~effect/Resource" as const * @see {@link get} for reading the currently stored acquisition result * @see {@link refresh} for forcing a new acquisition * + * @stability stable * @category models * @since 2.0.0 */ @@ -56,6 +58,7 @@ export interface Resource extends Pipeable { * * This predicate narrows the input to `Resource`. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -93,6 +96,7 @@ const makeUnsafe = ( * * @see {@link auto} for schedule-driven automatic refreshes * @see {@link refresh} to manually trigger a resource refresh + * @stability stable * @category constructors * @since 2.0.0 */ @@ -122,6 +126,7 @@ export const manual = ( * @see {@link manual} for caller-controlled refresh timing * @see {@link refresh} to trigger a refresh explicitly * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -148,6 +153,7 @@ export const auto = ( * * @see {@link refresh} to re-run acquisition and update the stored value before a later read * + * @stability stable * @category getters * @since 2.0.0 */ @@ -177,6 +183,7 @@ export const get = (self: Resource): Effect.Effect => * @see {@link manual} for resources refreshed only by caller action * @see {@link auto} for schedule-driven automatic refreshes * + * @stability stable * @category resource management * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Result.ts b/packages/effect/src/Result.ts similarity index 98% rename from repos/effect/packages/effect/src/Result.ts rename to packages/effect/src/Result.ts index 262a9493a..7b7460925 100644 --- a/repos/effect/packages/effect/src/Result.ts +++ b/packages/effect/src/Result.ts @@ -7,6 +7,7 @@ * for creating, checking, mapping, combining, and extracting results, plus * conversions to and from `Option` and nullable values. * + * @stability stable * @since 4.0.0 */ @@ -60,6 +61,7 @@ const TypeId = "~effect/Result" * @see {@link match} to fold both branches * @see {@link isSuccess} / {@link isFailure} for type guards * + * @stability stable * @category models * @since 4.0.0 */ @@ -90,6 +92,7 @@ export type Result = Success | Failure * @see {@link isFailure} to narrow the type * @see {@link Success} for the other variant * + * @stability stable * @category models * @since 4.0.0 */ @@ -118,6 +121,7 @@ export interface Failure extends Pipeable, Inspectable { * * @see {@link gen} for writing generator-based `Result` code that consumes this iterator protocol * + * @stability stable * @category generators * @since 4.0.0 */ @@ -152,6 +156,7 @@ export interface ResultIterator> { * @see {@link isSuccess} to narrow the type * @see {@link Failure} for the other variant * + * @stability stable * @category models * @since 4.0.0 */ @@ -177,6 +182,7 @@ export interface Success extends Pipeable, Inspectable { * This is an internal interface used by the Effect type system. You typically * do not need to reference it directly. * + * @stability stable * @category models * @since 4.0.0 */ @@ -192,6 +198,7 @@ export interface ResultUnify { * This is an internal interface used by the Effect type system. You typically * do not need to reference it directly. * + * @stability stable * @category models * @since 4.0.0 */ @@ -206,6 +213,7 @@ export interface ResultUnifyIgnore {} * (e.g., `map`, `flatMap` abstractions). You typically do not need to * reference this directly. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -234,6 +242,7 @@ export interface ResultTypeLambda extends TypeLambda { * const failure: E = "error" * ``` * + * @stability stable * @since 4.0.0 */ export declare namespace Result { @@ -272,6 +281,7 @@ export declare namespace Result { * @see {@link fail} to create a Failure * @see {@link void_ void} for a pre-built `Success` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -299,6 +309,7 @@ export const succeed: (right: A) => Result = result.succeed * @see {@link succeed} to create a Success * @see {@link mapError} to transform the error * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -329,6 +340,7 @@ export { * * @see {@link succeed} to create a Success with a specific value * + * @stability stable * @category constructors * @since 3.13.0 */ @@ -359,6 +371,7 @@ export { * * @see {@link fail} to create a Failure with a specific value * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -392,6 +405,7 @@ export const failVoid: Result = fail(void 0) * @see {@link fromOption} to convert from an Option * @see {@link succeed} / {@link fail} for direct construction * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -432,6 +446,7 @@ export const fromNullishOr: { * @see {@link getFailure} to extract the failure value as an Option * @see {@link fromNullishOr} to build a Result from nullable values * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -497,6 +512,7 @@ export { * @see {@link succeed} / {@link fail} for direct construction * @see {@link fromNullishOr} for nullable values * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -527,6 +543,7 @@ export { * * @see {@link isSuccess} / {@link isFailure} to narrow to a specific variant * + * @stability stable * @category guards * @since 4.0.0 */ @@ -559,6 +576,7 @@ export const isResult: (input: unknown) => input is Result = r * @see {@link isSuccess} for the opposite check * @see {@link isResult} to check if a value is any Result * + * @stability stable * @category guards * @since 4.0.0 */ @@ -591,6 +609,7 @@ export const isFailure: (self: Result) => self is Failure = re * @see {@link isFailure} for the opposite check * @see {@link isResult} to check if a value is any Result * + * @stability stable * @category guards * @since 4.0.0 */ @@ -622,6 +641,7 @@ export const isSuccess: (self: Result) => self is Success = re * @see {@link getFailure} to extract the error instead * @see {@link fromOption} for the reverse conversion * + * @stability stable * @category getters * @since 4.0.0 */ @@ -653,6 +673,7 @@ export const getSuccess: (self: Result) => Option = result.getSuc * @see {@link getSuccess} to extract the success instead * @see {@link fromOption} for the reverse conversion * + * @stability stable * @category getters * @since 4.0.0 */ @@ -682,6 +703,7 @@ export const getFailure: (self: Result) => Option = result.getFai * eq(Result.succeed(1), Result.fail("x")) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -726,6 +748,7 @@ export const makeEquivalence = ( * @see {@link mapError} to transform only the error value * @see {@link match} to fold into a single value * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -772,6 +795,7 @@ export const mapBoth: { * @see {@link map} to transform only the success value * @see {@link mapBoth} to transform both channels * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -813,6 +837,7 @@ export const mapError: { * @see {@link mapBoth} to transform both channels * @see {@link flatMap} when `f` returns a `Result` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -857,6 +882,7 @@ export const map: { * @see {@link merge} to extract `A | E` without mapping * @see {@link getOrElse} to unwrap only the success with a fallback * + * @stability stable * @category pattern matching * @since 2.0.0 */ @@ -909,6 +935,7 @@ export const match: { * @see {@link filterOrFail} to validate a value that is already in a `Result` * @see {@link fromNullishOr} for nullable-based construction * + * @stability stable * @category constructors * @since 3.4.0 */ @@ -968,6 +995,7 @@ export const liftPredicate: { * @see {@link liftPredicate} to create a `Result` from a raw value with a predicate * @see {@link flatMap} for general conditional chaining * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1015,6 +1043,7 @@ export const filterOrFail: { * @see {@link match} to map each branch to a common type * @see {@link getOrElse} to provide a fallback for failures * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1049,6 +1078,7 @@ export const merge: (self: Result) => E | A = match({ onFailure: ide * @see {@link match} to map both branches * @see {@link orElse} to recover with another Result instead of unwrapping * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1087,6 +1117,7 @@ export const getOrElse: { * @see {@link getOrUndefined} to return `undefined` instead * @see {@link getOrElse} for a custom fallback * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1118,6 +1149,7 @@ export const getOrNull: (self: Result) => A | null = getOrElse(const * @see {@link getOrNull} to return `null` instead * @see {@link getOrElse} for a custom fallback * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1156,6 +1188,7 @@ export const getOrUndefined: (self: Result) => A | undefined = getOr * @see {@link getOrThrow} to throw the raw failure value * @see {@link getOrElse} for a non-throwing alternative * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1196,6 +1229,7 @@ export const getOrThrowWith: { * @see {@link getOrThrowWith} for custom error mapping * @see {@link getOrElse} for a non-throwing alternative * + * @stability stable * @category getters * @since 2.0.0 */ @@ -1229,6 +1263,7 @@ export const getOrThrow: (self: Result) => A = getOrThrowWith(identi * @see {@link getOrElse} to unwrap with a fallback value (not a Result) * @see {@link mapError} to transform the error without recovering * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -1272,6 +1307,7 @@ export const orElse: { * @see {@link andThen} for a more flexible variant that also accepts plain values * @see {@link map} when `f` does not return a `Result` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1326,6 +1362,7 @@ export const flatMap: { * @see {@link flatMap} for the stricter variant (function returning Result only) * @see {@link map} when you always return a plain value * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1382,6 +1419,7 @@ export const andThen: { * @see {@link flatMap} for chaining two Results sequentially * @see {@link gen} for generator-based composition of multiple Results * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1448,6 +1486,7 @@ export const all: > | Record(self: Result): Result => * @see {@link flatMap} for point-free sequential composition * @see {@link all} to collect multiple independent Results * + * @stability stable * @category generators * @since 2.0.0 */ @@ -1538,6 +1578,7 @@ export const gen: Gen.Gen = (...args) => { * @see {@link gen} for an alternative generator-based syntax * @see {@link bindTo} for starting a do-notation chain from an existing Result * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1575,6 +1616,7 @@ export const Do: Result<{}> = succeed({}) * @see {@link let_ let} for pure computed fields * @see {@link bindTo} to wrap an initial Result into a named field * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1618,6 +1660,7 @@ export const bind: { * @see {@link Do} to start from an empty object * @see {@link bind} to add more fields * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1669,6 +1712,7 @@ export { * @see {@link Do} to start the do-notation chain * @see {@link bind} for Result-producing fields * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1701,6 +1745,7 @@ export { * * @see {@link transposeMapOption} to map and transpose in one step * + * @stability stable * @category transposing * @since 3.14.0 */ @@ -1742,6 +1787,7 @@ export const transposeOption = ( * * @see {@link transposeOption} when the Option already contains a Result * + * @stability stable * @category transposing * @since 3.15.0 */ @@ -1780,6 +1826,7 @@ export const transposeMapOption = dual< * @see {@link transposeOption} to transpose an Option that already contains a Result * @see {@link transposeMapOption} to map and transpose an Option in one step * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1803,6 +1850,7 @@ export const succeedNone = succeed(option_.none) * * @see {@link succeedNone} for the `None` counterpart * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1835,6 +1883,7 @@ export const succeedSome = (a: A): Result, E> => succeed * * @see {@link map} to transform the success value * + * @stability stable * @category mapping * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Runtime.ts b/packages/effect/src/Runtime.ts similarity index 98% rename from repos/effect/packages/effect/src/Runtime.ts rename to packages/effect/src/Runtime.ts index ab7ad6809..e9769d40c 100644 --- a/repos/effect/packages/effect/src/Runtime.ts +++ b/packages/effect/src/Runtime.ts @@ -7,6 +7,7 @@ * exit codes and already-reported failures. Application code usually calls the * platform-provided runner instead of using this module directly. * + * @stability stable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -54,6 +55,7 @@ import type * as Fiber from "./Fiber.ts" * await completed // => [Exit.succeed("Hello, World!"), 0] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -102,6 +104,7 @@ export interface Teardown { * * @see {@link errorExitCode} for customizing failure exit codes * + * @stability stable * @category running * @since 4.0.0 */ @@ -175,6 +178,7 @@ export const defaultTeardown: Teardown = ( * events // => ["Starting program", "Program completed", "Custom teardown logic"] * ``` * + * @stability stable * @category running * @since 4.0.0 */ @@ -238,6 +242,7 @@ declare global { * Use to type properties keyed by `Runtime.errorExitCode` on custom error * values. * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -279,6 +284,7 @@ export type errorExitCode = "~effect/Runtime/errorExitCode" * @see {@link defaultTeardown} for the default failure exit-code rules that read this marker * @see {@link getErrorExitCode} for reading the marker from unknown error values * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -305,6 +311,7 @@ export const errorExitCode: errorExitCode = "~effect/Runtime/errorExitCode" * * @see {@link errorExitCode} for the marker read by this function * + * @stability stable * @category getters * @since 4.0.0 */ @@ -326,6 +333,7 @@ export const getErrorExitCode = (u: unknown): number => { * Use to type properties keyed by `Runtime.errorReported` on custom error * values. * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -368,6 +376,7 @@ export type errorReported = "~effect/Runtime/errorReported" * @see {@link errorExitCode} for controlling failure exit codes * @see {@link getErrorReported} for reading the marker from unknown error values * + * @stability stable * @category symbols * @since 4.0.0 */ @@ -393,6 +402,7 @@ export const errorReported: errorReported = "~effect/Runtime/errorReported" * * @see {@link errorReported} for the marker read by this function * + * @stability stable * @category getters * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Schedule.ts b/packages/effect/src/Schedule.ts similarity index 98% rename from repos/effect/packages/effect/src/Schedule.ts rename to packages/effect/src/Schedule.ts index 144692a10..ccf572da2 100644 --- a/repos/effect/packages/effect/src/Schedule.ts +++ b/packages/effect/src/Schedule.ts @@ -6,6 +6,7 @@ * next step. Schedules are used by retry, repeat, stream, and channel APIs to * decide when work should continue, how long to wait, and when to stop. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyReadonlyArray } from "./Array.ts" @@ -47,6 +48,7 @@ const randomNext: Effect = random.Random.useSync((random) => random.next * await Effect.runPromise(Effect.provide(program, TestClock.layer())) // => [1, 2, 3] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -57,6 +59,7 @@ export interface Schedule { /** * Extended metadata that includes both input metadata and the output value from the schedule. * + * @stability stable * @category metadata * @since 4.0.0 */ @@ -90,6 +94,7 @@ export interface Metadata extends InputMetada * input and output values, zero duration, and zeroed timing fields before any * schedule step has produced metadata. * + * @stability stable * @category services * @since 4.0.0 */ @@ -109,6 +114,7 @@ export const CurrentMetadata = Context.Reference("effect/Schedule/Curr /** * The Schedule namespace contains types and utilities for working with schedules. * + * @stability stable * @since 2.0.0 */ export declare namespace Schedule { @@ -154,6 +160,7 @@ export declare namespace Schedule { /** * Extracts the output type from a `Schedule`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -162,6 +169,7 @@ export type Output = S extends Schedule ? Output /** * Extracts the input type from a `Schedule`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -170,6 +178,7 @@ export type Input = S extends Schedule ? Input : /** * Extracts the error type from a `Schedule`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -178,6 +187,7 @@ export type Error = S extends Schedule ? Error : /** * Extracts the service requirements from a `Schedule`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -211,6 +221,7 @@ const ScheduleProto = { * Schedule.isSchedule(undefined) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -244,6 +255,7 @@ export const isSchedule = (u: unknown): u is Schedule 0 * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -300,6 +312,7 @@ const metadataFn = () => { * await Effect.runPromise(program) // => "attempt 1: input" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -338,6 +351,7 @@ export const fromStepWithMetadata = ( * await Effect.runPromise(program) // => [Duration.millis(100), Duration.millis(100)] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -371,6 +385,7 @@ export const toStep = ( * @see {@link toStep} for manually supplying the timestamp and handling the returned delay yourself * @see {@link toStepWithSleep} for the same automatic sleeping behavior when only the schedule output is needed * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -423,6 +438,7 @@ export const toStepWithMetadata = ( * await Effect.runPromise(Effect.provide(program, TestClock.layer())) // => [0, 1] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -459,6 +475,7 @@ export const toStepWithSleep = ( * await Effect.runPromise(program) // => Duration.millis(25) * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -494,6 +511,7 @@ export const addDelay: { * Schedule.isSchedule(schedule) // => true * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -532,6 +550,7 @@ export const concat: { * Schedule.isSchedule(schedule) // => true * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -612,6 +631,7 @@ export const concatResult: { * Schedule.isSchedule(schedule) // => true * ``` * + * @stability stable * @category combining * @since 4.0.0 */ @@ -672,6 +692,7 @@ const maxDuration = (results: ReadonlyArray): Dur * Schedule.isSchedule(everyMinute) // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -714,6 +735,7 @@ export const cron: { * * @see {@link during} for recurring until a duration has elapsed * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -744,6 +766,7 @@ export const duration = (durationInput: Duration.Input): Schedule => * Schedule.isSchedule(schedule) // => true * ``` * + * @stability stable * @category combining * @since 4.0.0 */ @@ -844,6 +868,7 @@ const minDuration = (results: ReadonlyArray): Dur * await Effect.runPromise(program) // => [Duration.millis(100), Duration.millis(100)] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -876,6 +901,7 @@ export const exponential = ( * await Effect.runPromise(program) // => [Duration.millis(100), Duration.millis(100)] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -927,6 +953,7 @@ export const fibonacci = (one: Duration.Input): Schedule => { * * @see {@link spaced} for delaying after each action completes * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -981,6 +1008,7 @@ export const fixed = (interval: Duration.Input): Schedule => { * await Effect.runPromise(program) // => "Execution #1" * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1037,6 +1065,7 @@ export const map: { * await Effect.runPromise(program) // => Duration.millis(20) * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -1087,6 +1116,7 @@ export const modifyDelay: { * * @see {@link modifyDelay} for replacing recurrence delays with a custom effectful transformation * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -1119,6 +1149,7 @@ export const jittered = ( * await Effect.runPromise(program) // => "input" * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1163,6 +1194,7 @@ export const passthrough = ( * * @see {@link upTo} for limiting an existing schedule * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1192,6 +1224,7 @@ export const recurs = (times: number): Schedule => * * @see {@link fixed} for recurrence aligned to a regular cadence * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1228,6 +1261,7 @@ export const spaced = (duration: Duration.Input): Schedule => { * await Effect.runPromise(program) // => { attempts: [1], output: 0 } * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -1288,6 +1322,7 @@ export const tap: { * await Effect.runPromise(Effect.provide(program, TestClock.layer())) // => [1, 2, 3] * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1383,6 +1418,7 @@ export { * * @see {@link upTo} for stopping after a fixed number of schedule outputs * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -1417,6 +1453,7 @@ export { * await Effect.runPromise(program) // => [0, Duration.seconds(5)] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1454,6 +1491,7 @@ export const windowed = (interval: Duration.Input): Schedule => { * await Effect.runPromise(Effect.provide(program, TestClock.layer())) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1468,6 +1506,7 @@ export const forever: Schedule = spaced(Duration.zero) * `Effect.repeat`, the effect runs twice in total: once initially and once * after the schedule recurs. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1495,6 +1534,7 @@ export { * echoes its input values at each step. * * @see {@link forever} for an infinite schedule that returns incrementing step counts + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1524,6 +1564,7 @@ export { * Schedule.isSchedule(schedule) // => true * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Scheduler.ts b/packages/effect/src/Scheduler.ts similarity index 98% rename from repos/effect/packages/effect/src/Scheduler.ts rename to packages/effect/src/Scheduler.ts index a60495eb0..816b27084 100644 --- a/repos/effect/packages/effect/src/Scheduler.ts +++ b/packages/effect/src/Scheduler.ts @@ -7,6 +7,7 @@ * for queued tasks, and references for tuning or disabling automatic scheduler * yields. * + * @stability stable * @since 2.0.0 */ import * as Context from "./Context.ts" @@ -26,6 +27,7 @@ import type * as Fiber from "./Fiber.ts" * priorities, and decides when fibers should yield control after consuming * their operation budget. * + * @stability stable * @category services * @since 2.0.0 */ @@ -51,6 +53,7 @@ export interface Scheduler { * already scheduled tasks. Lower priority numbers run first, and equal * priorities run in FIFO order. * + * @stability stable * @category models * @since 4.0.0 */ @@ -72,6 +75,7 @@ export interface SchedulerDispatcher { * The default value creates a `MixedScheduler`. Provide this service to * customize execution mode, task dispatching, or yield behavior. * + * @stability stable * @category services * @since 2.0.0 */ @@ -156,6 +160,7 @@ class PriorityBuckets { * operation counts to decide when fibers should yield, and is the default * scheduler implementation. * + * @stability stable * @category models * @since 2.0.0 */ @@ -273,6 +278,7 @@ class MixedSchedulerDispatcher implements SchedulerDispatcher { * * @see {@link PreventSchedulerYield} for bypassing scheduler yield checks entirely rather than tuning the operation budget * + * @stability stable * @category services * @since 4.0.0 */ @@ -299,6 +305,7 @@ export const MaxOpsBeforeYield = Context.Reference("effect/Scheduler/Max * @see {@link MaxOpsBeforeYield} for tuning yield frequency without disabling yield checks * @see {@link Scheduler} for providing custom scheduler yield behavior * + * @stability stable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts similarity index 98% rename from repos/effect/packages/effect/src/Schema.ts rename to packages/effect/src/Schema.ts index de3428da8..1c4a323ad 100644 --- a/repos/effect/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -9,6 +9,7 @@ * generation, formatting, equivalence, optics, and differs derived from schema * definitions. * + * @stability stable * @since 4.0.0 */ @@ -87,6 +88,7 @@ const TypeId = InternalMake.TypeId * @see {@link optionalKey} — mark a struct field as optional * @see {@link optional} — mark a struct field as optional with `| undefined` * + * @stability stable * @category models * @since 4.0.0 */ @@ -96,6 +98,7 @@ export type Optionality = "required" | "optional" * * @see {@link mutableKey} — mark a struct field as mutable * + * @stability stable * @category models * @since 4.0.0 */ @@ -106,6 +109,7 @@ export type Mutability = "readonly" | "mutable" * @see {@link withConstructorDefault} — add a default to a schema field * @see {@link tag} — creates a literal field with a constructor default * + * @stability stable * @category models * @since 4.0.0 */ @@ -121,6 +125,7 @@ export type ConstructorDefault = "no-default" | "with-default" * @see {@link BottomWithoutNew.makeEffect} * @see {@link BottomWithoutNew.make} * + * @stability stable * @category options * @since 3.13.4 */ @@ -150,6 +155,7 @@ export interface MakeOptions { * Use as the base for schema interfaces that provide a specialized construct * signature. * + * @stability stable * @category models * @since 4.0.0 */ @@ -286,6 +292,7 @@ export interface BottomWithoutNew< * * @see {@link BottomWithoutNew} for the schema protocol without a construct signature * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -347,6 +354,7 @@ export interface Bottom< * @see {@link BottomWithoutNew} for the fully parameterized schema interface when every * view must be supplied directly. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -395,6 +403,7 @@ export interface BottomLazyWithoutNew< * * @see {@link BottomLazyWithoutNew} for the lazy schema protocol without a construct signature * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -424,6 +433,7 @@ export interface BottomLazy< /** * Type-level representation returned by {@link declareConstructor}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -492,6 +502,7 @@ export interface declareConstructor { value: 1 } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -521,6 +532,7 @@ export function declareConstructor() { /** * Type-level representation returned by {@link declare}. * + * @stability stable * @category constructors * @since 3.13.3 */ @@ -556,6 +568,7 @@ export interface declare extends declareConstructor( * type E = typeof bottom["Encoded"] // string * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -648,6 +662,7 @@ export function revealBottom( * * @see {@link annotateEncoded} to annotate the encoded side instead. * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -680,6 +695,7 @@ export function annotate(annotations: Annotations.Bottom(annotations: Annotations.Bottom "Username is required" * ``` * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -737,6 +754,7 @@ export function annotateKey(annotations: Annotations.Key` — for decode-only APIs * - {@link ConstraintEncoder}`` — for encode-only APIs * + * @stability stable * @category models * @since 4.0.0 */ @@ -778,6 +796,7 @@ export interface Top extends * * @see {@link Top} for the complete schema protocol. * + * @stability stable * @category models * @since 4.0.0 */ @@ -814,6 +833,7 @@ export interface Constraint { * @see {@link Constraint} for the generic lightweight schema constraint. * @see {@link Codec} for the full schema protocol with codec type views. * + * @stability stable * @category models * @since 4.0.0 */ @@ -837,6 +857,7 @@ export interface ConstraintCodec extends ConstraintCode * @see {@link ConstraintCodec} for APIs that need both decoded and encoded codec views. * @see {@link Codec} for the full schema protocol with codec type views. * + * @stability stable * @category models * @since 4.0.0 */ @@ -869,6 +891,7 @@ export interface ConstraintEncoder extends ConstraintCode * exposed by {@link Constraint}, but does not call methods such as `annotate`, * `check`, `rebuild`, `make`, or `makeEffect`. * + * @stability stable * @category models * @since 4.0.0 */ @@ -878,6 +901,7 @@ export interface ConstraintRebuildable extends Constraint { /** * Namespace of type-level helpers for {@link Schema}. * + * @stability stable * @since 3.10.0 */ export declare namespace Schema { @@ -929,6 +953,7 @@ export declare namespace Schema { * @see {@link Codec} — also tracks Encoded, DecodingServices, EncodingServices * @see {@link Schema.Type} — extract the decoded type at the type level * + * @stability stable * @category models * @since 3.10.0 */ @@ -939,6 +964,7 @@ export interface Schema extends Top { /** * Namespace of type-level helpers for {@link Codec}. * + * @stability stable * @since 4.0.0 */ export declare namespace Codec { @@ -1034,6 +1060,7 @@ export declare namespace Codec { * @see {@link Codec.EncodingServices} — extract required encoding services * @see {@link revealCodec} — helper to make TypeScript infer the full Codec type * + * @stability stable * @category models * @since 4.0.0 */ @@ -1056,6 +1083,7 @@ export interface Codec extends * @see {@link Codec} for preserving both decoded and encoded type information. * @see {@link Encoder} for the encode-only view. * + * @stability stable * @category models * @since 4.0.0 */ @@ -1078,6 +1106,7 @@ export interface Decoder extends Schema { * @see {@link Codec} for preserving both decoded and encoded type information. * @see {@link Decoder} for the decode-only view. * + * @stability stable * @category models * @since 4.0.0 */ @@ -1109,6 +1138,7 @@ export interface Encoder extends Schema { * type Enc = typeof codec["Encoded"] // string * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1130,6 +1160,7 @@ export function revealCodec(codec: Codec) { * `Optic` automatically. You normally interact with this interface through * {@link Optic_} utilities rather than constructing it directly. * + * @stability stable * @category models * @since 4.0.0 */ @@ -1166,6 +1197,7 @@ export interface Optic extends Schema { * ``` * * @see {@link isSchemaError} for narrowing unknown values + * @stability stable * @category errors * @since 4.0.0 */ @@ -1206,6 +1238,7 @@ export class SchemaError extends Data.TaggedError("SchemaError")<{ * Schema.isSchemaError(error) // => true * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1333,6 +1366,7 @@ function runSchemaErrorSync( * invalidResult.issues?.map((issue) => issue.path) // => [["name"], ["age"]] * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1353,6 +1387,7 @@ export function toStandardSchemaV1>( * * https://github.com/standard-schema/standard-schema/pull/134 * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1395,6 +1430,7 @@ export function toStandardJSONSchemaV1( * } * ``` * + * @stability stable * @category guards * @since 3.10.0 */ @@ -1444,6 +1480,7 @@ export const is: typeof SchemaParser.is = SchemaParser.is * } * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1467,6 +1504,7 @@ export const asserts: (schema: S, input: I) => asserts * * @see {@link SchemaParser.decodeUnknownEffect} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1497,6 +1535,7 @@ export function decodeUnknownEffect(schema: S, options?: S * * @see {@link SchemaParser.decodeEffect} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1534,6 +1573,7 @@ export const decodeEffect: ( * * @see {@link SchemaParser.decodeUnknownExit} for the adapter whose failure contains `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1570,6 +1610,7 @@ export function decodeUnknownExit>(schema: * * @see {@link SchemaParser.decodeExit} for the adapter whose failure contains `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1600,6 +1641,7 @@ export const decodeExit: >( * that contain defects, interruptions, or other non-schema reasons throw * instead. * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -1629,6 +1671,7 @@ export const decodeUnknownOption: >( * that contain defects, interruptions, or other non-schema reasons throw * instead. * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -1662,6 +1705,7 @@ export const decodeOption: >( * * @see {@link SchemaParser.decodeUnknownResult} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1696,6 +1740,7 @@ export function decodeUnknownResult>(schema * * @see {@link SchemaParser.decodeResult} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1728,6 +1773,7 @@ export const decodeResult: >( * * @see {@link SchemaParser.decodeUnknownPromise} for the adapter that rejects with an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -1764,6 +1810,7 @@ export function decodeUnknownPromise>( * * @see {@link SchemaParser.decodePromise} for the adapter that rejects with an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -1805,6 +1852,7 @@ export const decodePromise: >( * * @see {@link SchemaParser.decodeUnknownSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1837,6 +1885,7 @@ export function decodeUnknownSync>(schema: * * @see {@link SchemaParser.decodeSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1872,6 +1921,7 @@ export const decodeSync: >( * * @see {@link SchemaParser.encodeUnknownEffect} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1902,6 +1952,7 @@ export function encodeUnknownEffect(schema: S, options?: S * * @see {@link SchemaParser.encodeEffect} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1938,6 +1989,7 @@ export const encodeEffect: ( * * @see {@link SchemaParser.encodeUnknownExit} for the adapter whose failure contains `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1974,6 +2026,7 @@ export function encodeUnknownExit>(schema: * * @see {@link SchemaParser.encodeExit} for the adapter whose failure contains `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2004,6 +2057,7 @@ export const encodeExit: >( * that contain defects, interruptions, or other non-schema reasons throw * instead. * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -2034,6 +2088,7 @@ export const encodeUnknownOption: >( * that contain defects, interruptions, or other non-schema reasons throw * instead. * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -2066,6 +2121,7 @@ export const encodeOption: >( * * @see {@link SchemaParser.encodeUnknownResult} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2100,6 +2156,7 @@ export function encodeUnknownResult>(schema * * @see {@link SchemaParser.encodeResult} for the adapter that fails with `SchemaIssue.Issue` directly * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2131,6 +2188,7 @@ export const encodeResult: >( * * @see {@link SchemaParser.encodeUnknownPromise} for the adapter that rejects with an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -2167,6 +2225,7 @@ export function encodeUnknownPromise>( * * @see {@link SchemaParser.encodePromise} for the adapter that rejects with an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -2197,6 +2256,7 @@ export const encodePromise: >( * * @see {@link SchemaParser.encodeUnknownSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2227,6 +2287,7 @@ export function encodeUnknownSync>(schema: * * @see {@link SchemaParser.encodeSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2248,6 +2309,7 @@ export const encodeSync: >( * the bridge between the untyped AST representation and the strongly-typed * schema. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -2255,6 +2317,7 @@ export const make: (ast: S["ast"], options?: object) => S /** * Checks whether a value is a `Schema`. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -2264,6 +2327,7 @@ export function isSchema(u: unknown): u is Top { /** * Type-level representation returned by {@link optionalKey}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2311,6 +2375,7 @@ interface optionalKeyLambda extends Lambda { * type Person = typeof schema["Type"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2330,6 +2395,7 @@ interface requiredKeyLambda extends Lambda { * Use to remove optional-key wrapping from a schema field that was previously * wrapped with {@link optionalKey}. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2337,6 +2403,7 @@ export const requiredKey: requiredKeyLambda = Struct_.lambda( /** * Type-level representation returned by {@link optional}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -2373,6 +2440,7 @@ interface optionalLambda extends Lambda { * type Person = typeof schema.Type * ``` * + * @stability stable * @category combinators * @since 3.10.0 */ @@ -2397,6 +2465,7 @@ interface requiredLambda extends Lambda { * * This also unwraps the `UndefinedOr` member added by `optional`. * + * @stability stable * @category combinators * @since 3.10.0 */ @@ -2404,6 +2473,7 @@ export const required: requiredLambda = Struct_.lambda((self) => /** * Type-level representation returned by {@link mutableKey}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2436,6 +2506,7 @@ interface mutableKeyLambda extends Lambda { * Makes a struct field mutable (removes the `readonly` modifier on the property). * Use {@link readonlyKey} to reverse. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2455,6 +2526,7 @@ interface readonlyKeyLambda extends Lambda { * Use to remove mutable-key wrapping from a schema field that was previously * wrapped with {@link mutableKey}. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2462,6 +2534,7 @@ export const readonlyKey: readonlyKeyLambda = Struct_.lambda( /** * Type-level representation returned by {@link toType}. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2494,6 +2567,7 @@ interface toTypeLambda extends Lambda { * Extracts the type-side schema: sets `Encoded` to equal the decoded `Type`, * discarding the encoding transformation path. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2503,6 +2577,7 @@ export const toType: toTypeLambda = Struct_.lambda((schema) => /** * Type-level representation returned by {@link toEncoded}. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2535,6 +2610,7 @@ interface toEncodedLambda extends Lambda { * Extracts the encoded-side schema: sets `Type` to equal the `Encoded`, * discarding the decoding transformation path. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2545,6 +2621,7 @@ const FlipTypeId = "~effect/Schema/flip" /** * Type-level representation returned by {@link flip}. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2595,6 +2672,7 @@ function isFlip$(schema: Top): schema is flip { * Schema.decodeSync(flipped)(42) // => "42" * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2608,6 +2686,7 @@ export function flip(schema: S): flip { /** * Type-level representation returned by {@link Literal}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -2633,6 +2712,7 @@ export interface Literal * @see {@link Literals} for a schema that represents a union of literals. * @see {@link tag} for a schema that represents a literal value that can be * used as a discriminator field in tagged unions and has a constructor default. + * @stability stable * @category constructors * @since 3.10.0 */ @@ -2651,6 +2731,7 @@ export function Literal(literal: L): Literal(parts /** * Namespace for {@link TemplateLiteralParser} helper types. * + * @stability stable * @since 3.10.0 */ export declare namespace TemplateLiteralParser { @@ -2791,6 +2875,7 @@ export declare namespace TemplateLiteralParser { /** * Type-level representation returned by {@link TemplateLiteralParser}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -2841,6 +2926,7 @@ export interface TemplateLiteralParser exte * ``` * * @see {@link TemplateLiteral} for a validation-only version that keeps the string encoded. + * @stability stable * @category constructors * @since 3.10.0 */ @@ -2852,6 +2938,7 @@ export function TemplateLiteralParser /** * Type-level representation returned by {@link Enum}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2882,6 +2969,7 @@ export interface Enum * Schema.decodeSync(schema)(Direction.Up) // => "Up" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2898,6 +2986,7 @@ export function Enum(enums: A): Enum /** * Type-level representation of {@link Never}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -2905,6 +2994,7 @@ export interface Never extends Bottom * Schema for the `any` type. Accepts any value without validation. * * @see {@link Unknown} for a safer alternative that uses `unknown`. + * @stability stable * @category schemas * @since 3.10.0 */ @@ -2927,6 +3019,7 @@ export const Any: Any = make(SchemaAST.any) /** * Type-level representation of {@link Unknown}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -2940,6 +3033,7 @@ export interface Unknown extends Bottom * ``` * * @see {@link Symbol} for a schema that accepts any symbol. + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3210,6 +3326,7 @@ export function UniqueSymbol(symbol: sym): UniqueSymbo * - `Struct.MakeIn` — constructor input (optional/defaulted fields may be omitted) * - `Struct.DecodingServices` / `Struct.EncodingServices` — required services * + * @stability stable * @since 3.10.0 */ export declare namespace Struct { @@ -3344,6 +3461,7 @@ export declare namespace Struct { /** * Type-level representation returned by {@link Struct}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -3448,6 +3566,7 @@ function makeStruct(ast: SchemaAST.Objects, * Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 } * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -3487,6 +3606,7 @@ interface fieldsAssign extends Lambda { * Schema.decodeSync(schema)({ a: "a", c: 1 }) // => { a: "a", c: 1 } * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -3496,6 +3616,7 @@ export function fieldsAssign(fields: NewF /** * Type-level representation returned by {@link encodeKeys}. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -3540,6 +3661,7 @@ const canonicalPropertyKey = (key: PropertyKey): string | symbol => * Schema.decodeUnknownSync(Encoded)({ full_name: "Alice", age: 30 }) // => { name: "Alice", age: 30 } * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -3603,6 +3725,7 @@ export function encodeKeys< * alice.fullName // => "Alice Smith" * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -3649,6 +3772,7 @@ export function extendTo, const Fields extends S * - `Record.Type` — decoded type of the record * - `Record.Encoded` — encoded type of the record * + * @stability stable * @since 3.10.0 */ export declare namespace Record { @@ -3807,6 +3931,7 @@ export declare namespace Record { /** * Type-level representation returned by {@link Record}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -3861,6 +3986,7 @@ export interface $Record exten * Schema.decodeUnknownSync(schema)({ a: 1, b: 2 }) // => { a: 1, b: 2 } * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -3878,6 +4004,7 @@ export function Record( * - `StructWithRest.Type` — decoded type (struct type intersected with record types) * - `StructWithRest.Encoded` — encoded type * + * @stability stable * @since 4.0.0 */ export declare namespace StructWithRest { @@ -4030,6 +4157,7 @@ export declare namespace StructWithRest { /** * Type-level representation returned by {@link StructWithRest}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -4077,6 +4205,7 @@ export interface StructWithRest< * type T = typeof schema.Type * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4099,6 +4228,7 @@ export function StructWithRest< * - `Tuple.Encoded` — encoded tuple type * - `Tuple.MakeIn` — constructor input tuple * + * @stability stable * @since 3.10.0 */ export declare namespace Tuple { @@ -4212,6 +4342,7 @@ export declare namespace Tuple { /** * Type-level representation returned by {@link Tuple}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4281,6 +4412,7 @@ function makeTuple(ast: SchemaAST.Arrays, eleme * Schema.decodeUnknownSync(schema)(["hello", 42]) // => ["hello", 42] * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4297,6 +4429,7 @@ export function Tuple>(elements * - `TupleWithRest.Type` — decoded type (fixed elements + rest) * - `TupleWithRest.Encoded` — encoded type * + * @stability stable * @since 4.0.0 */ export declare namespace TupleWithRest { @@ -4414,6 +4547,7 @@ export declare namespace TupleWithRest { /** * Type-level representation returned by {@link TupleWithRest}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -4461,6 +4595,7 @@ export interface TupleWithRest< * Schema.decodeUnknownSync(schema)(["hello", 1, true, false]) // => ["hello", 1, true, false] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4473,6 +4608,7 @@ export function TupleWithRest, const Rest extend /** * Type-level representation returned by {@link Array}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -4516,6 +4652,7 @@ export { * Schema.decodeUnknownSync(schema)(["a", "b", "c"]) // => ["a", "b", "c"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4538,6 +4675,7 @@ export function withArrayLengthConstraints( /** * Type-level representation returned by {@link NonEmptyArray}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4574,6 +4712,7 @@ interface NonEmptyArrayLambda extends Lambda { * Schema.decodeUnknownSync(schema)([1, 2, 3]) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4583,6 +4722,7 @@ export const NonEmptyArray: NonEmptyArrayLambda = Struct_.lambda extends decodeTo<$Array(schema: S): ArrayEnsure { /** * Type-level representation returned by {@link UniqueArray}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -4644,6 +4786,7 @@ export interface UniqueArray extends $Array { * The equivalence used to determine uniqueness is the one provided by * `Schema.toEquivalence(item)`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4653,6 +4796,7 @@ export function UniqueArray(item: S): UniqueArray { /** * Type-level representation returned by {@link mutable}. * + * @stability stable * @category transforming * @since 3.10.0 */ @@ -4710,6 +4854,7 @@ interface mutableLambda extends Lambda { * value // => [1, 2, 3] * ``` * + * @stability stable * @category transforming * @since 3.10.0 */ @@ -4719,6 +4864,7 @@ export const mutable: mutableLambda = Struct_.lambda((schema) => /** * Type-level representation returned by {@link Union}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4802,6 +4948,7 @@ function makeUnion>( * Schema.decodeUnknownSync(schema)(42) // => 42 * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4814,6 +4961,7 @@ export function Union>( /** * Type-level representation returned by {@link Literals}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -4846,6 +4994,7 @@ export interface Literals> * ``` * * @see {@link Literal} for a schema that represents a single literal. + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4873,6 +5022,7 @@ export function Literals>( /** * Type-level representation returned by {@link NullOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4886,6 +5036,7 @@ interface NullOrLambda extends Lambda { /** * Creates a union schema of `S | null`. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4893,6 +5044,7 @@ export const NullOr: NullOrLambda = Struct_.lambda((self) => Union /** * Type-level representation returned by {@link UndefinedOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4906,6 +5058,7 @@ interface UndefinedOrLambda extends Lambda { /** * Creates a union schema of `S | undefined`. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4913,6 +5066,7 @@ export const UndefinedOr: UndefinedOrLambda = Struct_.lambda( /** * Type-level representation returned by {@link NullishOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4926,6 +5080,7 @@ interface NullishOrLambda extends Lambda { /** * Creates a union schema of `S | null | undefined`. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -4933,6 +5088,7 @@ export const NullishOr: NullishOrLambda = Struct_.lambda((self) /** * Type-level representation returned by {@link suspend}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -4978,6 +5134,7 @@ export interface suspend extends * Schema.decodeSync(Tree)({ value: 1, children: [] }) // => { value: 1, children: [] } * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -5000,6 +5157,7 @@ export function suspend(f: () => S): suspend { * Schema.is(AgeSchema)(121) // => false * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -5011,6 +5169,7 @@ export function check( /** * Type-level representation returned by {@link refine}. * + * @stability stable * @category filtering * @since 3.10.0 */ @@ -5047,6 +5206,7 @@ export interface refine extends * names type-level failures before the refinement runs; it does not name the * failed refinement itself. * + * @stability stable * @category filtering * @since 3.10.0 */ @@ -5066,6 +5226,7 @@ type FromBrandKeys> = A extends unknown ? Brand.Brand /** * Type-level representation returned by {@link brand}. * + * @stability stable * @category branding * @since 3.10.0 */ @@ -5112,6 +5273,7 @@ export interface brand extends * * @see {@link fromBrand} for applying a Brand constructor's checks along with its branded type * + * @stability stable * @category branding * @since 3.10.0 */ @@ -5135,6 +5297,7 @@ export function brand(identifier: B & EnsureSingleBrandKey) * * @see {@link brand} for adding a brand without constructor checks * + * @stability stable * @category branding * @since 3.10.0 */ @@ -5152,6 +5315,7 @@ export function fromBrand>( /** * Type-level representation returned by {@link middlewareDecoding}. * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5201,6 +5365,7 @@ export interface middlewareDecoding extends * ``` * * @see {@link catchDecoding} for a simpler error-recovery variant + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5219,6 +5384,7 @@ export function middlewareDecoding( /** * Type-level representation returned by {@link middlewareEncoding}. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -5268,6 +5434,7 @@ export interface middlewareEncoding extends * ``` * * @see {@link catchEncoding} for a simpler error-recovery variant + * @stability stable * @category encoding * @since 4.0.0 */ @@ -5303,6 +5470,7 @@ export function middlewareEncoding( * ``` * * @see {@link catchDecodingWithContext} to add service requirements to the handler + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5328,6 +5496,7 @@ export function catchDecoding( * @see {@link catchDecoding} for recovery handlers that do not require services * @see {@link middlewareDecoding} for intercepting or replacing the full decoding pipeline * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5346,6 +5515,7 @@ export function catchDecodingWithContext( * succeeds with a fallback value or re-fails with a (possibly different) issue. * * @see {@link catchEncodingWithContext} to add service requirements to the handler + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5371,6 +5541,7 @@ export function catchEncoding( * @see {@link catchEncoding} for recovery handlers that do not require services * @see {@link middlewareEncoding} for intercepting or replacing the full encoding pipeline * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5383,6 +5554,7 @@ export function catchEncodingWithContext( /** * Type-level representation returned by {@link decodeTo}. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5411,6 +5583,7 @@ export interface decodeTo extends * Schema.decodeUnknownSync(NumberFromString)("123") // => 123 * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5562,6 +5736,7 @@ export const BooleanLiterals = Literals([...TrueLiterals.literals, ...FalseLiter * Schema.decodeUnknownSync(Trimmed)(" hello ") // => "hello" * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5600,6 +5775,7 @@ export function decode(transformat * Schema.decodeSync(NumberFromString)("42") // => 42 * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5649,6 +5825,7 @@ export function encodeTo "HELLO" * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5667,6 +5844,7 @@ export function encode(transformat * * Only schemas that satisfy this constraint can be passed to {@link withConstructorDefault}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -5676,6 +5854,7 @@ export interface WithoutConstructorDefault { /** * Type-level representation returned by {@link withConstructorDefault}. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -5723,6 +5902,7 @@ export interface withConstructorDefault "anonymous" * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -5737,6 +5917,7 @@ export function withConstructorDefault * - `"passthrough"` (default): pass the value through during encoding * - `"omit"`: omit the key from the encoded output * + * @stability stable * @category options * @since 4.0.0 */ @@ -5796,6 +5978,7 @@ function toIssueEffect( * * @see {@link withDecodingDefault} for the value-level variant (key absent **or** `undefined`) * @see {@link withDecodingDefaultTypeKey} for the variant where the default is a `Type` value + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5814,6 +5997,7 @@ export function withDecodingDefaultKey( /** * Type-level representation returned by {@link withDecodingDefaultTypeKey}. * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5841,6 +6025,7 @@ export interface withDecodingDefaultTypeKey * * @see {@link withDecodingDefaultKey} for the variant where the default is an `Encoded` value * @see {@link withDecodingDefaultType} for the value-level variant + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5858,6 +6043,7 @@ export function withDecodingDefaultTypeKey( /** * Type-level representation returned by {@link withDecodingDefault}. * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -5899,6 +6085,7 @@ export interface withDecodingDefault extends de * * @see {@link withDecodingDefaultKey} for the key-level variant (key absent only, not `undefined`) * @see {@link withDecodingDefaultType} for the variant where the default is a `Type` value + * @stability stable * @category decoding * @since 3.10.0 */ @@ -5917,6 +6104,7 @@ export function withDecodingDefault( /** * Type-level representation returned by {@link withDecodingDefaultType}. * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5949,6 +6137,7 @@ export interface withDecodingDefaultType * * @see {@link withDecodingDefault} for the variant where the default is an `Encoded` value * @see {@link withDecodingDefaultTypeKey} for the key-level variant + * @stability stable * @category decoding * @since 4.0.0 */ @@ -5966,6 +6155,7 @@ export function withDecodingDefaultType( /** * Type-level representation returned by {@link tag}. * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -5989,6 +6179,7 @@ export interface tag extends withConstructor * * @see {@link tagDefaultOmit} to also omit the tag during encoding * @see {@link TaggedStruct} for a shorthand that adds `_tag` automatically + * @stability stable * @category constructors * @since 3.10.0 */ @@ -6023,6 +6214,7 @@ export function tag(literal: Tag): tag * ``` * * @see {@link tag} for the variant that keeps the tag during encoding + * @stability stable * @category constructors * @since 4.0.0 */ @@ -6032,6 +6224,7 @@ export function tagDefaultOmit(literal: Tag) /** * Type-level representation returned by {@link TaggedStruct}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -6082,6 +6275,7 @@ export type TaggedStruct "A" * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -6111,6 +6305,10 @@ type TaggedUnionUtils< Members > > = { + /** + * Property key used to discriminate union members. + */ + readonly tag: Tag /** * Discriminant values in flattened member order. */ @@ -6164,6 +6362,7 @@ type TaggedUnionUtils< /** * Type-level representation returned by {@link toTaggedUnion}. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -6198,6 +6397,7 @@ export type toTaggedUnion< * ``` * * @see {@link TaggedUnion} for a shorthand that builds the union from scratch + * @stability stable * @category combinators * @since 4.0.0 */ @@ -6213,7 +6413,7 @@ export function toTaggedUnion(tag: Tag) { walk(self) - return Object.assign(self, { cases, discriminants, isAnyOf, guards, match, matchOrElse }) as any + return Object.assign(self, { tag, cases, discriminants, isAnyOf, guards, match, matchOrElse }) as any function walk(schema: Constraint) { const ast = schema.ast @@ -6282,6 +6482,7 @@ export function toTaggedUnion(tag: Tag) { /** * Type-level representation returned by {@link TaggedUnion}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -6298,6 +6499,7 @@ export interface TaggedUnion> extends readonly "~type.make.in": { [K in keyof Cases]: Cases[K]["~type.make"] }[keyof Cases] readonly "~type.make": { [K in keyof Cases]: Cases[K]["~type.make"] }[keyof Cases] readonly "Iso": { [K in keyof Cases]: Cases[K]["Type"] }[keyof Cases] + readonly tag: "_tag" readonly cases: Cases readonly isAnyOf: ( keys: ReadonlyArray @@ -6327,7 +6529,7 @@ export interface TaggedUnion> extends /** * Builds a discriminated union from a record of field sets, one per variant. * Each key becomes the `_tag` literal and the value is passed to {@link TaggedStruct}. - * The result includes `cases`, `guards`, `isAnyOf`, `match`, and `matchOrElse` utilities. + * The result includes `tag`, `cases`, `guards`, `isAnyOf`, `match`, and `matchOrElse` utilities. * * **Example** (Pattern matching a discriminated union) * @@ -6348,6 +6550,7 @@ export interface TaggedUnion> extends * ``` * * @see {@link toTaggedUnion} to augment an existing union instead + * @stability stable * @category constructors * @since 4.0.0 */ @@ -6362,12 +6565,13 @@ export function TaggedUnion extends * person.name // => "Alice" * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -6424,6 +6629,7 @@ export function Opaque() { /** * Type-level representation returned by {@link instanceOf}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -6445,6 +6651,7 @@ export interface instanceOf extends declare { * decoded.toISOString() // => "2024-01-01T00:00:00.000Z" * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -6458,6 +6665,7 @@ export function instanceOf any, Iso = In * Constructs an `SchemaAST.Link` that describes how a value of type `T` encodes to and decodes from a `To` schema. * Used when building low-level AST transformations that bridge two schema types. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -6527,6 +6735,7 @@ export function link() { * } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -6551,6 +6760,7 @@ export const makeFilter: ( * `reportInput`) or a full {@link SchemaIssue.Issue} (returned unchanged); * the result is wrapped in an {@link SchemaIssue.Pointer} at the given `path`. * + * @stability stable * @category models * @since 3.10.0 */ @@ -6578,6 +6788,7 @@ export type FilterIssue = string | SchemaIssue.Issue | { * to returning that element directly; otherwise the entries are grouped * into an {@link SchemaIssue.Composite}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -6586,6 +6797,7 @@ export type FilterOutput = undefined | boolean | FilterIssue | ReadonlyArray SchemaAST.Filter`) check for any ordered type from an * `Order.Order` instance. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7244,6 +7477,7 @@ export function makeIsGreaterThan(options: { * Creates a greater-than-or-equal-to (`>=`) check for any ordered type from an * `Order.Order` instance. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7273,6 +7507,7 @@ export function makeIsGreaterThanOrEqualTo(options: { * Creates a less-than (`<`) check for any ordered type from an `Order.Order` * instance. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7303,6 +7538,7 @@ export function makeIsLessThan(options: { * Creates a less-than-or-equal-to (`<=`) check for any ordered type from an * `Order.Order` instance. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7332,6 +7568,7 @@ export function makeIsLessThanOrEqualTo(options: { * Creates an inclusive or exclusive range check for any ordered type from an * `Order.Order` instance. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7383,6 +7620,7 @@ export function makeIsBetween(deriveOptions: { * Creates a divisibility check for any numeric type from a remainder function * and a zero value. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7426,6 +7664,7 @@ function encodeNumberPayload(number: number): number { * `exclusiveMinimum` constraint to ensure generated numbers are greater than * the specified value. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7456,6 +7695,7 @@ export const isGreaterThan: (exclusiveMinimum: number, annotations?: Annotations * During arbitrary generation, this applies a `minimum` constraint * to ensure generated numbers are greater than or equal to the specified value. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7486,6 +7726,7 @@ export const isGreaterThanOrEqualTo: (minimum: number, annotations?: Annotations * `exclusiveMaximum` constraint to ensure generated numbers are less than the * specified value. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7516,6 +7757,7 @@ export const isLessThan: (exclusiveMaximum: number, annotations?: Annotations.Fi * During arbitrary generation, this applies a `maximum` constraint * to ensure generated numbers are less than or equal to the specified value. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7549,6 +7791,7 @@ export const isLessThanOrEqualTo: (maximum: number, annotations?: Annotations.Fi * `exclusiveMaximum` flags to ensure generated numbers fall within the * specified range. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7600,6 +7843,7 @@ export const isBetween: (options: { * * Throws a `RangeError` when `divisor` is zero or is not finite. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7639,6 +7883,7 @@ export function isMultipleOf( * * During arbitrary generation, this applies an integer constraint. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7663,6 +7908,7 @@ export function isInt(annotations?: Annotations.Filter) { /** * Type-level representation of {@link Int}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -7672,6 +7918,7 @@ export interface Int extends Number { /** * Schema for integers, rejecting `NaN`, `Infinity`, and `-Infinity`. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -7679,6 +7926,7 @@ export const Int: Int = Number.check(isInt()) /** * Type-level representation of {@link Natural}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -7694,6 +7942,7 @@ export interface Natural extends Int { * * @see {@link Int} for safe integers that may be negative * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -7714,6 +7963,7 @@ export const Natural: Natural = Int.check(isGreaterThanOrEqualTo(0)) * During arbitrary generation, this applies integer and range * constraints to ensure generated numbers are 32-bit signed integers. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7745,6 +7995,7 @@ export function isInt32(annotations?: Annotations.Filter) { * During arbitrary generation, this applies integer and range * constraints to ensure generated numbers are 32-bit unsigned integers. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7780,6 +8031,7 @@ function formatDateRuntime(date: globalThis.Date): string { * * During arbitrary generation, this applies an exclusive lower bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7815,6 +8067,7 @@ export const isGreaterThanDate: ( * * During arbitrary generation, this applies an inclusive lower bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7844,6 +8097,7 @@ export const isGreaterThanOrEqualToDate: ( * * During arbitrary generation, this applies an exclusive upper bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7879,6 +8133,7 @@ export const isLessThanDate: ( * * During arbitrary generation, this applies an inclusive upper bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7914,6 +8169,7 @@ export const isLessThanOrEqualToDate: ( * * During arbitrary generation, this applies the specified lower and upper bounds. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7956,6 +8212,7 @@ export const isBetweenDate: (options: { * * During arbitrary generation, this applies an exclusive lower bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -7986,6 +8243,7 @@ export const isGreaterThanBigInt: ( * * During arbitrary generation, this applies an inclusive lower bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8015,6 +8273,7 @@ export const isGreaterThanOrEqualToBigInt: ( * * During arbitrary generation, this applies an exclusive upper bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8045,6 +8304,7 @@ export const isLessThanBigInt: ( * * During arbitrary generation, this applies an inclusive upper bound. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8075,6 +8335,7 @@ export const isLessThanOrEqualToBigInt: ( * * During arbitrary generation, this applies the specified lower and upper bounds. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8140,6 +8401,7 @@ export const isBetweenBigInt: (options: { * Schema.is(NonEmptyArraySchema)([1]) // => true * ``` * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8192,6 +8454,7 @@ function makeIsMinLength(minLength: number, minCodePoints: number, annotations?: * During arbitrary generation, this applies a `minLength: 1` * constraint to ensure generated strings or arrays are non-empty. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8217,6 +8480,7 @@ export function isNonEmpty(annotations?: Annotations.Filter) { * constraint to ensure generated strings or arrays have at most the required * length. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8267,6 +8531,7 @@ export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) * `maxLength` constraints to ensure generated strings or arrays have a length * within the specified range. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8327,6 +8592,7 @@ export function isBetweenLength(minimum: number, maximum: number, annotations?: * @see {@link isMinLength} for counting UTF-16 code units * @see {@link isMaxCodePoints} * @see {@link isBetweenCodePoints} + * @stability stable * @category validation * @since 4.0.0 */ @@ -8365,6 +8631,7 @@ export function isMinCodePoints(minCodePoints: number, annotations?: Annotations * @see {@link isMaxLength} for counting UTF-16 code units * @see {@link isMinCodePoints} * @see {@link isBetweenCodePoints} + * @stability stable * @category validation * @since 4.0.0 */ @@ -8403,6 +8670,7 @@ export function isMaxCodePoints(maxCodePoints: number, annotations?: Annotations * @see {@link isBetweenLength} for counting UTF-16 code units * @see {@link isMinCodePoints} * @see {@link isMaxCodePoints} + * @stability stable * @category validation * @since 4.0.0 */ @@ -8463,6 +8731,7 @@ function normalizeCardinality(value: number): number { * * During arbitrary generation, this applies a node-local `minSize` constraint. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8503,6 +8772,7 @@ export function isMinSize(minSize: number, annotations?: Annotations.Filter) { * * During arbitrary generation, this applies a node-local `maxSize` constraint. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8543,6 +8813,7 @@ export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { * * During arbitrary generation, this applies node-local `minSize` and `maxSize` constraints. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8591,6 +8862,7 @@ export function isBetweenSize(minimum: number, maximum: number, annotations?: An * * During arbitrary generation, this applies a node-local `minProperties` constraint. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8633,6 +8905,7 @@ export function isMinProperties(minProperties: number, annotations?: Annotations * * During arbitrary generation, this applies a node-local `maxProperties` constraint. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8675,6 +8948,7 @@ export function isMaxProperties(maxProperties: number, annotations?: Annotations * * During arbitrary generation, this applies node-local `minProperties` and `maxProperties` constraints. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8721,6 +8995,7 @@ export function isBetweenProperties(minimum: number, maximum: number, annotation * names in the original input. Property transformations and excess-property * handling can therefore produce different results. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8770,6 +9045,7 @@ export function isPropertyNames(keySchema: Constraint, annotations?: Annotations * During arbitrary generation, this applies a node-local identity selector * for constructive uniqueness using Effect equality. * + * @stability stable * @category validation * @since 4.0.0 */ @@ -8803,6 +9079,7 @@ export function isUnique(annotations?: Annotations.Filter) { * JSON Schema has no equivalent constraint, so this check is omitted from generated documents. * * @see {@link isUnique} for validating uniqueness of complete array elements + * @stability stable * @category validation * @since 4.0.0 */ @@ -8826,6 +9103,7 @@ export function isUniqueKey(annotations?: Annotations.Filter) { /** * Type-level representation of {@link NonEmptyString}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -8836,6 +9114,7 @@ export interface NonEmptyString extends String { * Schema for non-empty strings. Validates that a string has at least one * character. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -8843,6 +9122,7 @@ export const NonEmptyString: NonEmptyString = String.check(isNonEmpty()) /** * Type-level representation of {@link Char}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -8865,6 +9145,7 @@ export interface Char extends String { * @see {@link NonEmptyString} for strings with length greater than zero * @see {@link isBetweenLength} for the underlying length check * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -8872,6 +9153,7 @@ export const Char: Char = String.check(isBetweenLength(1, 1)) /** * Type-level representation of {@link ErrorInstance}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -8881,6 +9163,7 @@ export interface ErrorInstance extends instanceOf { /** * Options for {@link ErrorInstance} and {@link Defect}. * + * @stability stable * @category options * @since 4.0.0 */ @@ -8937,6 +9220,7 @@ const errorSchemaCache: Array = [] * traces are omitted by default for security. Pass `{ includeStack: true }` to * include stack traces, or `{ excludeCause: true }` to omit causes. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -8967,6 +9251,7 @@ export function ErrorInstance(options?: ErrorOptions): ErrorInstance { /** * Type-level representation of {@link Defect}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -9013,6 +9298,7 @@ const defectSchemaCache: Array = [] * string representation. * * @see {@link ErrorInstance} for a schema that only accepts JavaScript `Error` values. + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9029,6 +9315,7 @@ export function Defect(options?: ErrorOptions): Defect { /** * Type-level representation returned by {@link ReadonlyMap}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9048,6 +9335,7 @@ export interface $ReadonlyMap * Iso representation used for `ReadonlyMap` schemas: an array of readonly * `[key, value]` tuples using each entry schema's `Iso` type. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -9058,6 +9346,7 @@ export type ReadonlyMapIso = R * Schema for readonly maps whose keys and values conform to the provided * schemas. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -9111,6 +9400,7 @@ export function ReadonlyMap( /** * Type-level representation returned by {@link ReadonlySet}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9129,6 +9419,7 @@ export interface $ReadonlySet extends * Iso representation used for `ReadonlySet` schemas: an array of element values * using the element schema's `Iso` type. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -9136,6 +9427,7 @@ export type ReadonlySetIso = ReadonlyArray(value: Value): $ReadonlySe /** * Type-level representation of {@link RegExp}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9199,6 +9492,7 @@ export interface RegExp extends instanceOf { * * The default JSON serializer encodes a `RegExp` as `{ source, flags }`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9243,6 +9537,7 @@ export const RegExp: RegExp = instanceOf( /** * Type-level representation of {@link URL}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9260,6 +9555,7 @@ const URLString = String.annotate({ expected: "a string that will be decoded as * * - encodes `URL` as a `string` * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9285,6 +9581,7 @@ export const URL: URL = instanceOf( /** * Type-level representation of {@link URLFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9302,6 +9599,7 @@ export interface URLFromString extends decodeTo { * Encoding: * - A `URL` is encoded as a `string` * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9309,6 +9607,7 @@ export const URLFromString: URLFromString = URLString.pipe(decodeTo(URL, SchemaT /** * Type-level representation of {@link Date}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9342,6 +9641,7 @@ const DateString = String.annotate({ expected: "a string that will be decoded as * @see {@link DateFromString} for decoding strings into Date instances * @see {@link DateFromMillis} for decoding epoch milliseconds into Date instances * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9367,6 +9667,7 @@ export const Date: Date = declare( /** * Type-level representation of {@link DateFromString}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -9395,6 +9696,7 @@ export interface DateFromString extends decodeTo { * @see {@link DateTimeUtcFromString} for decoding date-time strings into UTC values * @see {@link Date} for accepting Date instances directly * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -9402,6 +9704,7 @@ export const DateFromString: DateFromString = DateString.pipe(decodeTo(Date, Sch /** * Type-level representation of {@link DateFromMillis}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9433,6 +9736,7 @@ export interface DateFromMillis extends decodeTo { * @see {@link DateFromString} for decoding string-encoded dates * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9442,6 +9746,7 @@ export const DateFromMillis: DateFromMillis = Int.pipe( /** * Type-level representation returned by {@link fromJsonString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9479,6 +9784,7 @@ const JsonString = String.annotate({ * Schema.encodeSync(schemaFromJsonString)({ a: 1 }) // => "{\n \"a\": 1\n}" * ``` * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9497,6 +9803,7 @@ export const UnknownFromJsonString: fromJsonString = fromJsonString(Unk /** * Type-level representation of {@link File}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9511,6 +9818,7 @@ export interface File extends instanceOf { * The default JSON serializer encodes a `File` as `{ data, type, name, lastModified }` * where `data` is base64-encoded. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9574,6 +9882,7 @@ export const File: File = instanceOf(globalThis.File, { /** * Type-level representation of {@link FormData}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9588,6 +9897,7 @@ export interface FormData extends instanceOf { * The default JSON serializer encodes a `FormData` as an array of `[key, entry]` * pairs where each entry is tagged as `"String"` or `"File"`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9637,6 +9947,7 @@ export const FormData: FormData = instanceOf(globalThis.FormData, { /** * Type-level representation returned by {@link fromFormData}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9722,6 +10033,7 @@ export interface fromFormData extends decodeTo { a: 1 } * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -9731,6 +10043,7 @@ export function fromFormData(schema: S): fromFormData { /** * Type-level representation of {@link URLSearchParams}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9744,6 +10057,7 @@ export interface URLSearchParams extends instanceOf * * The default JSON serializer encodes a `URLSearchParams` as a query string. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9769,6 +10083,7 @@ export const URLSearchParams: URLSearchParams = instanceOf(globalThis.URLSearchP /** * Type-level representation returned by {@link fromURLSearchParams}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9849,6 +10164,7 @@ export interface fromURLSearchParams extends decodeTo { a: 1 } * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -9858,6 +10174,7 @@ export function fromURLSearchParams(schema: S): fromURLSea /** * Type-level representation of {@link NumberFromString}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -9878,6 +10195,7 @@ export interface NumberFromString extends decodeTo { * Encoding: * A number is encoded as a `string`. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -9887,6 +10205,7 @@ export const NumberFromString: NumberFromString = String.annotate({ /** * Type-level representation of {@link FiniteFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9905,6 +10224,7 @@ export interface FiniteFromString extends decodeTo { * Encoding: * - A finite number is encoded as a `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9914,6 +10234,7 @@ export const FiniteFromString: FiniteFromString = String.annotate({ /** * Type-level representation of {@link BigIntFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -9945,6 +10266,7 @@ export interface BigIntFromString extends decodeTo { * @see {@link NumberFromString} for parsing JavaScript number strings, including non-finite values * @see {@link BigDecimalFromString} for parsing decimal number strings * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -9954,6 +10276,7 @@ export const BigIntFromString: BigIntFromString = make(SchemaAST.bigIntS /** * Type-level representation of {@link Trimmed}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -9963,6 +10286,7 @@ export interface Trimmed extends String { /** * Schema for strings that contains no leading or trailing whitespaces. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -9970,6 +10294,7 @@ export const Trimmed: Trimmed = String.check(isTrimmed()) /** * Type-level representation of {@link Trim}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -9987,6 +10312,7 @@ export interface Trim extends decodeTo { * Encoding: * - The trimmed string is encoded as is. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -9996,6 +10322,7 @@ export const Trim: Trim = String.annotate({ /** * Type-level representation of {@link StringFromBase64}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10013,6 +10340,7 @@ export interface StringFromBase64 extends decodeTo { * Encoding: * - A `string` is encoded as a base64-encoded string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10024,6 +10352,7 @@ export const StringFromBase64: StringFromBase64 = String.annotate({ /** * Type-level representation of {@link StringFromBase64Url}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10041,6 +10370,7 @@ export interface StringFromBase64Url extends decodeTo { * Encoding: * - A `string` is encoded as a base64 (URL) encoded string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10052,6 +10382,7 @@ export const StringFromBase64Url: StringFromBase64Url = String.annotate({ /** * Type-level representation of {@link StringFromHex}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10069,6 +10400,7 @@ export interface StringFromHex extends decodeTo { * Encoding: * - A `string` is encoded as a hex string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10080,6 +10412,7 @@ export const StringFromHex: StringFromHex = String.annotate({ /** * Type-level representation of {@link StringFromUriComponent}. * + * @stability stable * @category models * @since 3.12.0 */ @@ -10115,6 +10448,7 @@ export interface StringFromUriComponent extends decodeTo { * Schema.encodeSync(UrlSchema)({ maxItemPerPage: 10, page: 1 }) // => "%7B%22maxItemPerPage%22%3A10%2C%22page%22%3A1%7D" * ``` * + * @stability stable * @category schemas * @since 3.12.0 */ @@ -10127,6 +10461,7 @@ export const StringFromUriComponent: StringFromUriComponent = String.annotate({ * Schema for property keys accepted by Effect schemas: finite `number`, * `symbol`, or `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -10139,6 +10474,7 @@ export const PropertyKey: Union = Union([Fini * The result contains an `issues` array where each issue has a message and an * optional path made of property keys or keyed path segments. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -10169,6 +10505,7 @@ export const StandardSchemaV1FailureResult: Struct<{ /** * Type-level representation of {@link BooleanFromBit}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -10191,6 +10528,7 @@ export interface BooleanFromBit extends decodeTo { * Encoding: * - A `Uint8Array` is encoded as a base64-encoded string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10276,6 +10618,7 @@ export const Uint8ArrayFromBase64: Uint8ArrayFromBase64 = Base64String.pipe( /** * Type-level representation of {@link Uint8ArrayFromBase64Url}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10294,6 +10637,7 @@ export interface Uint8ArrayFromBase64Url extends decodeTo { * Encoding: * - A `Uint8Array` is encoded as a base64 (URL) encoded string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10308,6 +10652,7 @@ export const Uint8ArrayFromBase64Url: Uint8ArrayFromBase64Url = String.annotate( /** * Type-level representation of {@link Uint8ArrayFromHex}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10326,6 +10671,7 @@ export interface Uint8ArrayFromHex extends decodeTo { * Encoding: * - A `Uint8Array` is encoded as a hex encoded string. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10361,6 +10707,7 @@ const bigDecimalFromString: SchemaTransformation.Transformation { * @see {@link BigIntFromString} for parsing base-10 integer strings into bigint values * @see {@link NumberFromString} for parsing JavaScript number strings * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -10529,6 +10884,7 @@ export const BigDecimalFromString: BigDecimalFromString = BigDecimalString.pipe( /** * Type-level representation of {@link ByteSize}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -10544,6 +10900,7 @@ export interface ByteSize extends declare { * beyond JavaScript's safe-integer range. The StringTree codec uses the * human-readable byte-size syntax exposed by {@link ByteSizeFromString}. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -10579,6 +10936,7 @@ const ByteSizeString = String.annotate({ expected: "a string that will be decode /** * Type-level representation of {@link ByteSizeFromString}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -10611,6 +10969,7 @@ export interface ByteSizeFromString extends decodeTo { * A unit is required. Signs, exponent notation, digit separators, and ambiguous * unit spellings such as `KB`, `mb`, `Mb`, or `b` are rejected. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -10620,6 +10979,7 @@ export const ByteSizeFromString: ByteSizeFromString = ByteSizeString.pipe( /** * Type-level representation of {@link ByteSizeFromBigInt}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -10629,6 +10989,7 @@ export interface ByteSizeFromBigInt extends decodeTo { /** * Schema that decodes non-negative bigint byte counts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -10638,6 +10999,7 @@ export const ByteSizeFromBigInt: ByteSizeFromBigInt = BigInt.pipe( /** * Type-level representation of {@link ByteSizeFromNumber}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -10651,6 +11013,7 @@ export interface ByteSizeFromNumber extends decodeTo { * * Encoding fails when the byte count exceeds `Number.MAX_SAFE_INTEGER`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -10665,6 +11028,7 @@ export const ByteSizeFromNumber: ByteSizeFromNumber = Number.pipe( /** * Type-level representation returned by {@link CauseReason}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -10688,6 +11052,7 @@ export interface CauseReason extends * Failures are represented with a `Fail` tag and encoded error, defects with a * `Die` tag and encoded defect, and interrupts with an optional `fiberId`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -10718,6 +11083,7 @@ export type CauseReasonIso = { * @see {@link Cause} for constructing schemas for full Cause values * @see {@link CauseReasonIso} for the ISO shape of each cause reason * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -10807,6 +11173,7 @@ export function CauseReason( /** * Type-level representation returned by {@link Cause}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10834,6 +11201,7 @@ export interface Cause extends * @see {@link Cause} for constructing schemas for full Cause values * @see {@link CauseReasonIso} for the ISO shape of each array element * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -10856,6 +11224,7 @@ export type CauseIso = ReadonlyArray * @see {@link CauseReason} for the schema used by each individual cause reason * @see {@link CauseIso} for the ordered array representation used by the schema ISO * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -10914,6 +11283,7 @@ function chunkLink(values: Schema>) { /** * Type-level representation returned by {@link Chunk}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -10939,6 +11309,7 @@ export interface Chunk extends * * @see {@link Chunk} for the schema interface and constructor that use this ISO representation * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -10946,6 +11317,7 @@ export type ChunkIso = ReadonlyArray /** * Schema for chunks whose values conform to the provided element schema. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11128,6 +11500,7 @@ function timeZoneArbitrarySchema(): Codec { /** * Type-level representation of {@link DateTimeUtc}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -11152,6 +11525,7 @@ export interface DateTimeUtc extends declare { * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values * @see {@link DateTimeZoned} for preserving zoned DateTime values * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11190,6 +11564,7 @@ export const DateTimeUtc: DateTimeUtc = declare( /** * Type-level representation of {@link DateTimeUtcFromDate}. * + * @stability stable * @category models * @since 3.12.0 */ @@ -11217,6 +11592,7 @@ export interface DateTimeUtcFromDate extends decodeTo { * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values * @see {@link Date} for validating Date instances without converting them * + * @stability stable * @category schemas * @since 3.12.0 */ @@ -11229,6 +11605,7 @@ export const DateTimeUtcFromDate: DateTimeUtcFromDate = Date.pipe( /** * Type-level representation of {@link DateTimeUtcFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -11253,6 +11630,7 @@ export interface DateTimeUtcFromString extends decodeTo { * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values * @see {@link DateFromString} for decoding strings into JavaScript Date instances * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -11267,6 +11645,7 @@ export const DateTimeUtcFromString: DateTimeUtcFromString = String.annotate({ /** * Type-level representation of {@link DateTimeUtcFromMillis}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -11288,6 +11667,7 @@ export interface DateTimeUtcFromMillis extends decodeTo * @see {@link DateTimeUtcFromString} for decoding date-time strings into UTC values * @see {@link DateFromMillis} for decoding epoch milliseconds into JavaScript Date instances * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -11300,6 +11680,7 @@ export const DateTimeUtcFromMillis: DateTimeUtcFromMillis = Int.pipe( /** * Type-level representation of {@link TimeZoneOffset}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -11315,6 +11696,7 @@ export interface TimeZoneOffset extends declare { * * - encodes `DateTime.TimeZone.Offset` as a number (offset in milliseconds) * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11342,6 +11724,7 @@ export const TimeZoneOffset: TimeZoneOffset = declare( /** * Type-level representation of {@link TimeZoneNamed}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -11358,6 +11741,7 @@ const TimeZoneNamedString = String.annotate({ expected: "an IANA time zone ident * * - encodes `DateTime.TimeZone.Named` as a string (IANA time zone identifier) * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11390,6 +11774,7 @@ export const TimeZoneNamed: TimeZoneNamed = declare( /** * Type-level representation of {@link TimeZoneNamedFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -11407,6 +11792,7 @@ export interface TimeZoneNamedFromString extends decodeTo * Encoding: * - A `DateTime.TimeZone.Named` is encoded as a `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -11416,6 +11802,7 @@ export const TimeZoneNamedFromString: TimeZoneNamedFromString = TimeZoneNamedStr /** * Type-level representation of {@link TimeZone}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -11435,6 +11822,7 @@ const TimeZoneString = String.annotate({ * - encodes `DateTime.TimeZone` as a string (IANA identifier or offset like * `+03:00`) * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11469,6 +11857,7 @@ export const TimeZone: TimeZone = declare( /** * Type-level representation of {@link TimeZoneFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -11486,6 +11875,7 @@ export interface TimeZoneFromString extends decodeTo { * Encoding: * - A `DateTime.TimeZone` is encoded as a `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -11495,6 +11885,7 @@ export const TimeZoneFromString: TimeZoneFromString = TimeZoneString.pipe( /** * Type-level representation of {@link DateTimeZoned}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -11516,6 +11907,7 @@ const DateTimeZonedString = String.annotate({ * - encodes named zones by appending the IANA identifier in brackets, such as * `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]` * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -11560,6 +11952,7 @@ export const DateTimeZoned: DateTimeZoned = declare( /** * Type-level representation of {@link DateTimeZonedFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -11577,6 +11970,7 @@ export interface DateTimeZonedFromString extends decodeTo * Encoding: * - A `DateTime.Zoned` is encoded as a `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -12982,6 +13376,7 @@ const durationFromMillis: SchemaTransformation.Transformation { * Schema.decodeUnknownSync(Schema.Duration)(Duration.seconds(5)) // => Duration.seconds(5) * ``` * + * @stability stable * @category schemas * * @since 3.10.0 @@ -13062,6 +13458,7 @@ const DurationString = String.annotate({ expected: "a string that will be decode /** * Type-level representation of {@link DurationFromString}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -13080,6 +13477,7 @@ export interface DurationFromString extends decodeTo { * Encoding: * - A `Duration` is encoded as a parseable `string`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -13089,6 +13487,7 @@ export const DurationFromString: DurationFromString = DurationString.pipe( /** * Type-level representation of {@link DurationFromNanos}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13109,6 +13508,7 @@ export interface DurationFromNanos extends decodeTo { * fails when the duration cannot be represented as nanoseconds, such as * `Duration.infinity` or `Duration.negativeInfinity`. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -13118,6 +13518,7 @@ export const DurationFromNanos: DurationFromNanos = BigInt.pipe( /** * Type-level representation of {@link DurationFromMillis}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13140,6 +13541,7 @@ export interface DurationFromMillis extends decodeTo { * * `NaN` is decoded as `Duration.zero`, matching `Duration.millis`. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -13154,6 +13556,7 @@ export const DurationFromMillis: DurationFromMillis = Number.pipe( /** * Type-level representation returned by {@link Exit}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13178,6 +13581,7 @@ export interface Exit = Graph_.Snapshot /** * Iso representation used for {@link Graph} schemas. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -13302,6 +13709,7 @@ export type GraphIso( * encoded.edges // => [{ index: 0, source: 0, target: 1, data: 1 }] * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13563,6 +13972,7 @@ function hashMapLink(entries: Schema exten * Iso representation used for `HashMap` schemas: an array of readonly * `[key, value]` tuples using each entry schema's `Iso` type. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -13591,6 +14002,7 @@ export type HashMapIso = Reado /** * Schema for hash maps whose keys and values conform to the provided schemas. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -13658,6 +14070,7 @@ function hashSetLink(values: Schema>) { /** * Type-level representation returned by {@link HashSet}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13676,6 +14089,7 @@ export interface HashSet extends * Iso representation used for `HashSet` schemas: an array of element values * using the element schema's `Iso` type. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -13683,6 +14097,7 @@ export type HashSetIso = ReadonlyArray /** * Schema for hash sets whose values conform to the provided element schema. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -13740,6 +14155,7 @@ export function HashSet(value: Value): HashSet /** * Type-level representation returned by {@link Option}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13762,6 +14178,7 @@ export interface Option extends * `None` is represented as `{ _tag: "None" }`, while `Some` is represented as * `{ _tag: "Some", value }` using the wrapped schema's `Iso` type. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -13774,6 +14191,7 @@ export type OptionIso = { /** * Schema for `Option` values. * + * @stability stable * @category schemas * @since 3.10.0 */ @@ -13828,6 +14246,7 @@ export function Option(value: A): Option { /** * Type-level representation returned by {@link OptionFromNullOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13842,6 +14261,7 @@ export interface OptionFromNullOr extends decodeTo(schema: S): OptionFromNul /** * Type-level representation returned by {@link OptionFromUndefinedOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13869,6 +14290,7 @@ export interface OptionFromUndefinedOr extends decodeTo(schema: S): OptionFr /** * Type-level representation returned by {@link OptionFromNullishOr}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -13897,6 +14320,7 @@ export interface OptionFromNullishOr extends decodeTo extends decodeTo(schema: S): OptionFr /** * Type-level representation returned by {@link OptionFromOptional}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -14302,6 +14729,7 @@ export interface OptionFromOptional extends decodeTo(schema: S): OptionFromO /** * Type-level representation returned by {@link OptionFromOptionalNullOr}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -14333,6 +14762,7 @@ export interface OptionFromOptionalNullOr * according to `options.onNoneEncoding`: `"omit"` encodes a missing key, * `null` encodes `null`, and `undefined` encodes `undefined`. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -14364,6 +14794,7 @@ export function OptionFromOptionalNullOr( /** * Type-level representation returned by {@link Redacted}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -14398,6 +14829,7 @@ type NormalizedRedactedOptions = * sensitive and should not be exposed in JSON. * * @see {@link RedactedFromValue} for decoding raw values and wrapping them in `Redacted`. + * @stability stable * @category schemas * @since 3.10.0 */ @@ -14485,6 +14917,7 @@ export function Redacted(value: S, options?: { /** * Type-level representation returned by {@link RedactedFromValue}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -14497,6 +14930,7 @@ export interface RedactedFromValue extends decodeTo(value: S, options?: { /** * Type-level representation returned by {@link Result}. * + * @stability stable * @category models * @since 4.0.0 */ @@ -14551,6 +14986,7 @@ export interface Result extends * Successful results are represented as `{ _tag: "Success", success }`, while * failed results are represented as `{ _tag: "Failure", failure }`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -14564,6 +15000,7 @@ export type ResultIso = { /** * Schema for `Result` values. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -14628,6 +15065,7 @@ export function Result( /** * Type-level representation returned by {@link Class}. * + * @stability stable * @category models * @since 3.10.0 */ @@ -14967,6 +15405,7 @@ function isStruct(schema: Struct.Fields | Struct): schema is Stru * @see {@link Error} for defining schema-backed error classes * @see {@link TaggedError} for defining tagged schema-backed error classes * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -15026,6 +15465,7 @@ export const Class: { * c.radius // => 5 * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -15085,6 +15525,7 @@ export const TaggedClass: { * error.id // => 1 * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -15145,6 +15586,7 @@ export const Error: { * error.id // => 42 * ``` * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -15192,6 +15634,7 @@ export const TaggedError: { * The annotation is applied through this helper because adding it directly to * `Annotations.Bottom` would make schemas invariant. * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -15208,6 +15651,7 @@ export function overrideToFormatter(toFormatter: () => Formatter< * The optional `onBefore` hook lets you intercept specific AST nodes before * the default formatting logic runs. * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -15227,6 +15671,7 @@ export function toFormatter(schema: S, options?: { * Use when you need a custom equivalence instead of the default structural * equivalence derived by {@link toEquivalence}. * + * @stability stable * @category instances * @since 4.0.0 */ @@ -15249,6 +15694,7 @@ export function overrideToEquivalence(toEquivalence: () => Equiva * eq({ id: 1, name: "Alice" }, { id: 2, name: "Alice" }) // => false * ``` * + * @stability stable * @category instances * @since 4.0.0 */ @@ -15273,6 +15719,7 @@ export function toEquivalence(schema: Schema): Equivalence.Equivalence * * @see {@link SchemaRepresentation.toRepresentation} for converting a `SchemaAST.AST` directly * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15296,6 +15743,7 @@ export function toRepresentation( * `SchemaRepresentation.toJsonSchemaMultiDocument`, reference allocation has already happened and `referencePolicy` * has no effect. * + * @stability stable * @category options * @since 4.0.0 */ @@ -15435,6 +15883,7 @@ export interface ToJsonSchemaOptions extends SchemaRepresentation.ToRepresentati * @see {@link toCodecJson} for decoding and encoding the canonical JSON representation * @see {@link SchemaRepresentation.toJsonSchemaDocument} for compiling an existing live representation document * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15451,6 +15900,7 @@ export function toJsonSchemaDocument( /** * Type-level representation returned by {@link toCodecJson}. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15496,6 +15946,7 @@ export interface toCodecJson extends * checks and annotations remain on its source node rather than being copied to * the JSON target. Source checks still run after the transformation. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15516,6 +15967,7 @@ export const toCodecJson: { * Links cannot require services because the returned `Codec` does not expose * service requirements. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15526,6 +15978,7 @@ export const toCodecIso: { * A {@link Tree} of `string | undefined` nodes. Leaf values are either a * string representation or `undefined` for opaque/declaration types. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15533,6 +15986,7 @@ export type StringTree = Tree /** * Type-level representation returned by {@link toCodecStringTree}. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15574,6 +16028,7 @@ export interface toCodecStringTree extends * `toCodec` encoding. A callback can return `undefined` when the declaration is * already in canonical StringTree form. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15583,6 +16038,7 @@ export const toCodecStringTree: { /** * Type-level representation returned by {@link toCodecArrayFromSingle}. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15621,6 +16077,7 @@ export interface toCodecArrayFromSingle extends * decoding convenience rather than a canonical StringTree representation. It * does not parse comma-separated strings. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15648,6 +16105,7 @@ type XmlEncoderOptions = { * an `Effect` that succeeds with the XML string or fails with a * {@link SchemaIssue.Issue} if codec encoding fails. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -15674,6 +16132,7 @@ export function toEncoderXml( * `SchemaIssue.makeFormatterDefault()`. Consume {@link toCodecIso} with an * effectful parser for asynchronous execution or explicit failure handling. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15683,6 +16142,7 @@ export function toIso(schema: S): Optic_.Iso(schema: S): Optic_.Iso(schema: S): Optic_.Iso extends * provided `decode` and `encode` getters to transform between the schema type * and the target codec. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -15776,6 +16239,7 @@ export function overrideToCodecIso( * `SchemaIssue.makeFormatterDefault()`. Invalid patch operations instead produce * {@link JsonPatch.apply} errors. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -15786,6 +16250,7 @@ export function toDifferJsonPatch(schema: ConstraintCodec): Diffe * Recursive tree type whose leaves are `Node` values and whose branches are * readonly arrays or string-keyed records of child trees. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15794,6 +16259,7 @@ export type Tree = Node | TreeRecord | ReadonlyArray> * A record node in a {@link Tree}: an object mapping string keys to child * `Tree` nodes. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15805,6 +16271,7 @@ export interface TreeRecord { * The resulting schema accepts a single node value, an array of trees, or an * object whose values are trees. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -15828,6 +16295,7 @@ export function Tree(node: S) { * readonly record of `string → Json`. For the corresponding schema, see the * {@link Json} const. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15835,6 +16303,7 @@ export type Json = null | number | boolean | string | JsonArray | JsonObject /** * A readonly array of {@link Json} values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15842,6 +16311,7 @@ export interface JsonArray extends ReadonlyArray {} /** * A readonly record whose values are {@link Json} values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15859,6 +16329,7 @@ export interface JsonObject { * Schema.decodeUnknownOption(Schema.Json)({ key: [1, true, null] }) // => Option.some({ key: [1, true, null] }) * ``` * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -15885,6 +16356,7 @@ export const Json: Codec = make(SchemaAST.annotate(SchemaAST.Json, { * ``` * * @see {@link Json} for a schema that also accepts JSON arrays and primitive values + * @stability stable * @category schemas * @since 4.0.0 */ @@ -15893,6 +16365,7 @@ export const JsonObject: $Record> = Reco * Recursive TypeScript type for mutable JSON values: `null`, `number`, * `boolean`, `string`, mutable arrays, or mutable string-keyed records. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15900,6 +16373,7 @@ export type MutableJson = null | number | boolean | string | MutableJsonArray | /** * A mutable array of {@link MutableJson} values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15907,6 +16381,7 @@ export interface MutableJsonArray extends Array {} /** * A mutable record whose values are {@link MutableJson} values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -15924,6 +16399,7 @@ const JsonError = Struct({ * Schema that accepts any mutable JSON-compatible value. See {@link Json} for * the immutable variant. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -15939,6 +16415,7 @@ export const MutableJson: Codec = make(SchemaAST.annotate(SchemaAST * annotations are taken from the last check; otherwise they are taken from * the base schema instance. * + * @stability stable * @category getters * @since 4.0.0 */ @@ -15952,6 +16429,7 @@ export function resolveAnnotations( * annotations are those attached via `annotateKey` and live on the AST's * `context` rather than on the schema node itself. * + * @stability stable * @category getters * @since 4.0.0 */ @@ -15968,6 +16446,7 @@ export function resolveAnnotationsKey(schema: S): Annotati * Use {@link resolveAnnotations} to read the annotations attached to a schema at * runtime. * + * @stability stable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts similarity index 98% rename from repos/effect/packages/effect/src/SchemaAST.ts rename to packages/effect/src/SchemaAST.ts index e9eaae850..4bf42ef43 100644 --- a/repos/effect/packages/effect/src/SchemaAST.ts +++ b/packages/effect/src/SchemaAST.ts @@ -8,6 +8,7 @@ * ASTs programmatically, change encoded or decoded views, collect issues, or * run low-level schema checks. * + * @stability stable * @since 4.0.0 */ @@ -46,6 +47,7 @@ import type * as Types from "./Types.ts" * - Discriminate on the `_tag` field (e.g. `"String"`, `"Objects"`, `"Union"`). * * @see {@link isAST} + * @stability stable * @category models * @since 4.0.0 */ @@ -85,6 +87,7 @@ function makeGuard(tag: T) { * objects. * * @see {@link AST} + * @stability stable * @category guards * @since 4.0.0 */ @@ -102,6 +105,7 @@ export function isAST(u: unknown): u is AST { * * @see {@link Declaration} for the AST node type narrowed by this guard * + * @stability stable * @category guards * @since 3.10.0 */ @@ -119,6 +123,7 @@ export const isDeclaration = makeGuard("Declaration") * @see {@link null_ null} for the singleton `Null` AST instance * @see {@link isLiteral} for exact primitive literal AST nodes * + * @stability stable * @category guards * @since 4.0.0 */ @@ -134,6 +139,7 @@ export const isNull = makeGuard("Null") * * @see {@link isVoid} for narrowing AST nodes that represent TypeScript `void` instead of exact `undefined` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -149,6 +155,7 @@ export const isUndefined = makeGuard("Undefined") * * @see {@link isUndefined} for narrowing AST nodes that represent the literal `undefined` value instead of TypeScript `void` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -165,6 +172,7 @@ export const isVoid = makeGuard("Void") * @see {@link Never} for the AST node type narrowed by this guard * @see {@link never} for the singleton `Never` AST instance * + * @stability stable * @category guards * @since 4.0.0 */ @@ -180,6 +188,7 @@ export const isNever = makeGuard("Never") * * @see {@link isAny} for the guard for the `Any` node, whose parsed result is typed as `any` rather than `unknown` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -195,6 +204,7 @@ export const isUnknown = makeGuard("Unknown") * * @see {@link isUnknown} for the guard for the `Unknown` node, whose parsed result is typed as `unknown` rather than `any` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -212,6 +222,7 @@ export const isAny = makeGuard("Any") * @see {@link string} for the singleton `String` AST instance * @see {@link isLiteral} for exact primitive literal AST nodes, including exact string literals * + * @stability stable * @category guards * @since 4.0.0 */ @@ -225,6 +236,7 @@ export const isString = makeGuard("String") * Use to detect `Number` AST nodes while inspecting, traversing, or transforming * schema ASTs. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -241,6 +253,7 @@ export const isNumber = makeGuard("Number") * @see {@link Boolean} for the AST node type matched by this guard * @see {@link boolean} for the singleton instance to use when constructing a boolean AST directly * + * @stability stable * @category guards * @since 4.0.0 */ @@ -256,6 +269,7 @@ export const isBoolean = makeGuard("Boolean") * @see {@link BigInt} for the AST node matched by this guard * @see {@link bigInt} for the singleton instance; use `isBigInt` when narrowing an existing `AST` value * + * @stability stable * @category guards * @since 4.0.0 */ @@ -271,6 +285,7 @@ export const isBigInt = makeGuard("BigInt") * * @see {@link isUniqueSymbol} for the sibling guard that narrows the `UniqueSymbol` variant for one exact symbol value * + * @stability stable * @category guards * @since 4.0.0 */ @@ -286,6 +301,7 @@ export const isSymbol = makeGuard("Symbol") * @see {@link Literal} for the AST node type narrowed by this guard * @see {@link LiteralValue} for the values stored by literal nodes * + * @stability stable * @category guards * @since 3.10.0 */ @@ -294,6 +310,7 @@ export const isLiteral = makeGuard("Literal") /** * Narrows an {@link AST} to {@link UniqueSymbol}. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -311,6 +328,7 @@ export const isUniqueSymbol = makeGuard("UniqueSymbol") * @see {@link objectKeyword} for the singleton `ObjectKeyword` AST instance * @see {@link isObjects} for struct and record AST nodes * + * @stability stable * @category guards * @since 3.10.0 */ @@ -326,6 +344,7 @@ export const isObjectKeyword = makeGuard("ObjectKeyword") * * @see {@link Enum} for the AST node type narrowed by this guard * + * @stability stable * @category guards * @since 4.0.0 */ @@ -334,6 +353,7 @@ export const isEnum = makeGuard("Enum") /** * Narrows an {@link AST} to {@link TemplateLiteral}. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -349,6 +369,7 @@ export const isTemplateLiteral = makeGuard("TemplateLiteral") * * @see {@link Arrays} for the AST node type narrowed by this guard * + * @stability stable * @category guards * @since 4.0.0 */ @@ -357,6 +378,7 @@ export const isArrays = makeGuard("Arrays") /** * Narrows an {@link AST} to {@link Objects}. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -365,6 +387,7 @@ export const isObjects = makeGuard("Objects") /** * Narrows an {@link AST} to {@link Union}. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -373,6 +396,7 @@ export const isUnion = makeGuard("Union") /** * Narrows an {@link AST} to {@link Suspend}. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -394,6 +418,7 @@ export const isSuspend = makeGuard("Suspend") * * @see {@link Encoding} * @see {@link decodeTo} + * @stability stable * @category models * @since 4.0.0 */ @@ -407,6 +432,7 @@ export interface Link { /** * Constructs a {@link Link}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -443,6 +469,7 @@ export const Link: new( * * @see {@link Link} * @see {@link toEncoded} + * @stability stable * @category models * @since 4.0.0 */ @@ -473,6 +500,7 @@ export type Encoding = readonly [Link, ...Array] * Object property order is unspecified, including in values passed to checks. * Decoding and encoding do not guarantee preservation of input key order. * + * @stability stable * @category options * @since 3.10.0 */ @@ -591,6 +619,7 @@ export const defaultParseOptions: ParseOptions = {} * * @see `Schema.optionalKey` * @see {@link isOptional} + * @stability stable * @category models * @since 4.0.0 */ @@ -605,6 +634,7 @@ export interface Context { /** * Constructs a {@link Context}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -646,6 +676,7 @@ export const Context: new( * @see {@link Check} * @see {@link Filter} * @see {@link FilterGroup} + * @stability stable * @category models * @since 4.0.0 */ @@ -707,6 +738,7 @@ type DeclarationRun = ( * complete synchronously. * * @see {@link isDeclaration} + * @stability stable * @category models * @since 4.0.0 */ @@ -737,6 +769,7 @@ export interface Declaration extends ASTNode { /** * Constructs a {@link Declaration}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -822,6 +855,7 @@ export const Declaration: new( * * @see {@link null_ null} * @see {@link isNull} + * @stability stable * @category models * @since 4.0.0 */ @@ -838,6 +872,7 @@ export interface Null extends ASTNode { /** * Constructs a {@link Null}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -868,6 +903,7 @@ export { * Use when you need the shared AST node for exact null values while inspecting * or constructing schema ASTs. * + * @stability stable * @category constants * @since 4.0.0 */ @@ -883,6 +919,7 @@ export { * * @see {@link undefined} * @see {@link isUndefined} + * @stability stable * @category models * @since 4.0.0 */ @@ -902,6 +939,7 @@ export interface Undefined extends ASTNode { /** * Constructs a {@link Undefined}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -944,6 +982,7 @@ export { * Use when you need the shared AST node for exact undefined values while * inspecting or constructing schema ASTs. * + * @stability stable * @category constants * @since 4.0.0 */ @@ -967,6 +1006,7 @@ export { * @see {@link undefined} for the AST singleton that matches only exact `undefined` * @see {@link void_ void} * @see {@link isVoid} + * @stability stable * @category models * @since 4.0.0 */ @@ -988,6 +1028,7 @@ export interface Void extends ASTNode { /** * Constructs a {@link Void}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1032,6 +1073,7 @@ export { * @see {@link undefined} for the sibling AST singleton that matches exactly `undefined` * @see {@link isVoid} for narrowing an AST to a `Void` node * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1048,6 +1090,7 @@ export { * * @see {@link never} * @see {@link isNever} + * @stability stable * @category models * @since 4.0.0 */ @@ -1064,6 +1107,7 @@ export interface Never extends ASTNode { /** * Constructs a {@link Never}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1095,6 +1139,7 @@ export const Never: new( * @see {@link Never} for the AST node class * @see {@link isNever} for narrowing an AST to a `Never` node * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1106,6 +1151,7 @@ export const never = new Never() * @see {@link any} * @see {@link isAny} * + * @stability stable * @category models * @since 4.0.0 */ @@ -1122,6 +1168,7 @@ export interface Any extends ASTNode { /** * Constructs a {@link Any}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1152,6 +1199,7 @@ export const Any: new( * * @see {@link unknown} for the sibling AST singleton that also accepts every value while preserving the safer `unknown` type * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1167,6 +1215,7 @@ export const any = new Any() * * @see {@link unknown} * @see {@link isUnknown} + * @stability stable * @category models * @since 4.0.0 */ @@ -1183,6 +1232,7 @@ export interface Unknown extends ASTNode { /** * Constructs a {@link Unknown}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1213,6 +1263,7 @@ export const Unknown: new( * * @see {@link any} for the singleton that accepts every value as `any` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1225,6 +1276,7 @@ export const unknown = new Unknown() * @see {@link objectKeyword} * @see {@link isObjectKeyword} * + * @stability stable * @category models * @since 4.0.0 */ @@ -1241,6 +1293,7 @@ export interface ObjectKeyword extends ASTNode { /** * Constructs a {@link ObjectKeyword}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1272,6 +1325,7 @@ export const ObjectKeyword: new( * @see {@link ObjectKeyword} for the AST node class * @see {@link isObjectKeyword} for narrowing an AST to an `ObjectKeyword` node * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -1286,6 +1340,7 @@ export const objectKeyword = new ObjectKeyword() * `string | number`. Parsing succeeds when the input matches any enum value. * * @see {@link isEnum} + * @stability stable * @category models * @since 4.0.0 */ @@ -1311,6 +1366,7 @@ export interface Enum extends ASTNode { /** * Constructs a {@link Enum}. Numeric values must be finite. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1434,6 +1490,7 @@ function isTemplateLiteralPart( * strings at runtime. * * @see {@link isTemplateLiteral} + * @stability stable * @category models * @since 4.0.0 */ @@ -1465,6 +1522,7 @@ export interface TemplateLiteral extends ASTNode { * Throws if a part contains an encoding, including inside unions or nested * template literals. Parts must describe their values without transformations. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1588,6 +1646,7 @@ function templateLiteralTransformation(template: TemplateLiteral) { * `symbol`. * * @see {@link isUniqueSymbol} + * @stability stable * @category models * @since 4.0.0 */ @@ -1608,6 +1667,7 @@ export interface UniqueSymbol extends ASTNode { /** * Constructs a {@link UniqueSymbol}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1656,6 +1716,7 @@ export const UniqueSymbol: new( * * @see {@link Literal} * + * @stability stable * @category models * @since 3.10.0 */ @@ -1684,6 +1745,7 @@ export type LiteralValue = string | number | boolean | bigint * * @see {@link LiteralValue} * @see {@link isLiteral} + * @stability stable * @category models * @since 4.0.0 */ @@ -1710,6 +1772,7 @@ export interface Literal extends ASTNode { /** * Constructs a {@link Literal}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1777,6 +1840,7 @@ function literalToString(ast: Literal): Literal { * @see {@link string} * @see {@link isString} * + * @stability stable * @category models * @since 4.0.0 */ @@ -1796,6 +1860,7 @@ export interface String extends ASTNode { /** * Constructs a {@link String}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1831,6 +1896,7 @@ export const String: new( * @see {@link String} for the AST node class * @see {@link isString} for narrowing an AST to a string node * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1852,6 +1918,7 @@ export const string = new String() * * @see {@link number} * @see {@link isNumber} + * @stability stable * @category models * @since 4.0.0 */ @@ -1880,6 +1947,7 @@ export interface Number extends ASTNode { /** * Constructs a {@link Number}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1949,6 +2017,7 @@ function hasCheck(checks: ReadonlyArray>, id: string): boolean { * @see {@link Number} for the AST node class and serialization behavior * @see {@link Literal} for exact finite numeric literal AST nodes * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1960,6 +2029,7 @@ export const number = new Number() * @see {@link boolean} * @see {@link isBoolean} * + * @stability stable * @category models * @since 4.0.0 */ @@ -1976,6 +2046,7 @@ export interface Boolean extends ASTNode { /** * Constructs a {@link Boolean}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2007,6 +2078,7 @@ export const Boolean: new( * @see {@link Boolean} for the AST node class * @see {@link Literal} for exact boolean literal AST nodes * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2027,6 +2099,7 @@ export const boolean = new Boolean() * * @see {@link symbol} * @see {@link isSymbol} + * @stability stable * @category models * @since 4.0.0 */ @@ -2049,6 +2122,7 @@ export interface Symbol extends ASTNode { /** * Constructs a {@link Symbol}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2093,6 +2167,7 @@ export const Symbol: new( * * @see {@link UniqueSymbol} for an AST node that matches one specific symbol * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2108,6 +2183,7 @@ export const symbol = new Symbol() * * @see {@link bigInt} * @see {@link isBigInt} + * @stability stable * @category models * @since 4.0.0 */ @@ -2130,6 +2206,7 @@ export interface BigInt extends ASTNode { /** * Constructs a {@link BigInt}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2172,6 +2249,7 @@ export const BigInt: new( * @see {@link BigInt} for the AST node class and string-codec behavior * @see {@link isBigInt} for narrowing an AST to a `BigInt` node * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2216,6 +2294,7 @@ export const bigInt = new BigInt() * * @see {@link isArrays} * @see {@link Objects} + * @stability stable * @category models * @since 4.0.0 */ @@ -2245,6 +2324,7 @@ export interface Arrays extends ASTNode { /** * Constructs a {@link Arrays}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2565,6 +2645,7 @@ export function getIndexSignatureKeys( * {@link Context}. * * @see {@link Objects} + * @stability stable * @category models * @since 4.0.0 */ @@ -2576,6 +2657,7 @@ export interface PropertySignature { /** * Constructs a {@link PropertySignature}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2653,6 +2735,7 @@ function isIndexSignatureParameter(ast: AST): ast is IndexSignatureParameter { * * @see {@link Objects} * @see {@link PropertySignature} + * @stability stable * @category models * @since 4.0.0 */ @@ -2664,6 +2747,7 @@ export interface IndexSignature { /** * Constructs a {@link IndexSignature}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2725,6 +2809,7 @@ export const IndexSignature: new(parameter: AST, type: AST) => IndexSignature = * @see {@link PropertySignature} * @see {@link IndexSignature} * @see {@link Arrays} + * @stability stable * @category models * @since 4.0.0 */ @@ -2753,6 +2838,7 @@ export interface Objects extends ASTNode { /** * Constructs a {@link Objects}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3583,6 +3669,7 @@ export function getCandidateIndex(types: ReadonlyArray): CandidateIndex { * ``` * * @see {@link isUnion} + * @stability stable * @category models * @since 4.0.0 */ @@ -3611,6 +3698,7 @@ export interface Union extends ASTNode { /** * Local matching options stored on {@link Union} nodes. * + * @stability stable * @category options * @since 4.0.0 */ @@ -3622,6 +3710,7 @@ export interface UnionOptions { /** * Constructs a {@link Union}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3800,7 +3889,14 @@ function parseUnionCandidates( if (state.out) return state.out return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options)) } - return resumeUnion(eff, state) + if (effectIsExit(eff)) return resumeUnion(eff, state) + // Reparse on later runs to avoid reusing mutable traversal state. + let first = true + return Effect.suspend(() => { + if (!first) return parseUnionCandidates(ast, parser, candidates, input, options) + first = false + return resumeUnion(eff, state) + }) } function resumeUnion( @@ -3883,6 +3979,7 @@ function formatIsOptional(isOptional: boolean | undefined): string { * ``` * * @see {@link isSuspend} + * @stability stable * @category models * @since 4.0.0 */ @@ -3903,6 +4000,7 @@ export interface Suspend extends ASTNode { /** * Constructs a {@link Suspend}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3973,6 +4071,7 @@ export const Suspend: new( * @see {@link FilterGroup} * @see {@link Check} * @see {@link isPattern} + * @stability stable * @category models * @since 4.0.0 */ @@ -3992,6 +4091,7 @@ export interface Filter extends Pipeable.Pipeable { /** * Constructs a {@link Filter}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4046,6 +4146,7 @@ export const Filter: new( * * @see {@link Filter} * @see {@link Check} + * @stability stable * @category models * @since 4.0.0 */ @@ -4063,6 +4164,7 @@ export interface FilterGroup extends Pipeable.Pipeable { /** * Constructs a {@link FilterGroup}. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4104,6 +4206,7 @@ export const FilterGroup: new( * * @see {@link Filter} * @see {@link FilterGroup} + * @stability stable * @category models * @since 4.0.0 */ @@ -4202,6 +4305,7 @@ const numberToJson = new Link( * ``` * * @see {@link Filter} + * @stability stable * @category constructors * @since 4.0.0 */ @@ -4465,6 +4569,7 @@ export function withConstructorDefault( * @see {@link Link} * @see {@link Encoding} * @see {@link flip} + * @stability stable * @category transforming * @since 4.0.0 */ @@ -4532,6 +4637,7 @@ export function record(key: AST, value: AST): Objects { * * @see `Schema.optionalKey` * @see {@link Context} + * @stability stable * @category predicates * @since 4.0.0 */ @@ -4591,6 +4697,7 @@ function canPreserveEncodingChecks(ast: AST): boolean { * * @see {@link toEncoded} * @see {@link flip} + * @stability stable * @category transforming * @since 4.0.0 */ @@ -4638,6 +4745,7 @@ export const toType = memoizeIdempotent((ast: A): A => { * * @see {@link toType} * @see {@link flip} + * @stability stable * @category transforming * @since 4.0.0 */ @@ -4677,6 +4785,7 @@ function flipEncoding(ast: AST, encoding: Encoding): AST { * * @see {@link toType} * @see {@link toEncoded} + * @stability stable * @category transforming * @since 4.0.0 */ @@ -5018,6 +5127,7 @@ export function getConstructorDescriptor(ast: AST): ConstructorDescriptor | unde * @see {@link resolveIdentifier} * @see {@link resolveTitle} * @see {@link resolveDescription} + * @stability stable * @category annotations * @since 4.0.0 */ @@ -5032,6 +5142,7 @@ export const resolve: (ast: AST) => Schema.Annotations.Annotations | undefined = * are present. Returns `undefined` if the key is not found. * * @see {@link resolve} + * @stability stable * @category annotations * @since 4.0.0 */ @@ -5047,6 +5158,7 @@ export const resolveAt: (key: string) => (ast: AST) => A | undefined = Intern * * @see {@link resolve} * @see {@link resolveTitle} + * @stability stable * @category annotations * @since 4.0.0 */ @@ -5059,6 +5171,7 @@ export const resolveIdentifier: (ast: AST) => string | undefined = InternalAnnot * @see {@link resolveIdentifier} * @see {@link resolveDescription} * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -5071,6 +5184,7 @@ export const resolveTitle: (ast: AST) => string | undefined = InternalAnnotation * @see {@link resolveTitle} * @see {@link resolveIdentifier} * + * @stability stable * @category annotations * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SchemaGetter.ts b/packages/effect/src/SchemaGetter.ts similarity index 97% rename from repos/effect/packages/effect/src/SchemaGetter.ts rename to packages/effect/src/SchemaGetter.ts index 2293489e9..84a1ced85 100644 --- a/repos/effect/packages/effect/src/SchemaGetter.ts +++ b/packages/effect/src/SchemaGetter.ts @@ -9,6 +9,7 @@ * and ready-made conversions for common string, number, binary, date, form, and * URL-related values. * + * @stability stable * @since 4.0.0 */ import * as Arr from "./Array.ts" @@ -31,6 +32,7 @@ import * as Str from "./String.ts" /** * A transformation that returns its input unchanged. * + * @stability stable * @category models * @since 4.0.0 */ @@ -41,6 +43,7 @@ export interface Passthrough extends Pipeable.Pipeable { /** * A synchronous transformation of present values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -52,6 +55,7 @@ export interface Transform extends Pipeable.Pipeable { /** * A synchronous transformation of optional values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -63,6 +67,7 @@ export interface TransformOptional extends Pipeable.Pipeable { /** * An effectful transformation of present values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -77,6 +82,7 @@ export interface TransformEffect extends Pipeable.Pipeable { /** * An effectful transformation of optional values. * + * @stability stable * @category models * @since 4.0.0 */ @@ -122,6 +128,7 @@ export interface TransformOptionalEffect extends Pipeable.Pipeab * @see {@link transform} to create a getter from a pure function * @see {@link transformEffect} for effectful transformation * + * @stability stable * @category models * @since 4.0.0 */ @@ -175,6 +182,7 @@ const runGetter = ( * result // => Option.some(42) * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -228,6 +236,7 @@ const composeOptionalEffect = ( * Effect.runSync(SchemaGetter.run(getter, Option.some("-1"), {})) // => Option.some(0) * ``` * + * @stability stable * @category combining * @since 4.0.0 */ @@ -308,6 +317,7 @@ export const compose: { * Effect.runSync(SchemaGetter.run(getter, Option.some("21"), {})) // => Option.some(42) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -341,6 +351,7 @@ export const map: { * @see {@link transform} when you need to use the input value * @see {@link passthrough} when you want to keep the input as-is * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -379,6 +390,7 @@ export function succeed(t: T): Getter { * @see {@link forbidden} for a convenience helper for `Forbidden` issues * @see {@link checkEffect} to fail conditionally based on input value * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -418,6 +430,7 @@ export function fail( * * @see {@link fail} to fail with a custom issue type * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -455,6 +468,7 @@ export function forbidden(message: (oe: Option.Option) => string): Gett * * @see {@link forbidden} for a forbidden getter with a custom message * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -496,6 +510,7 @@ const passthrough_: Passthrough = makeGetter({ _tag: "Passthrough" }) * @see {@link passthroughSubtype} when `E extends T` * @see {@link transform} when you need to change the value * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -530,6 +545,7 @@ export function passthrough(): Getter { * @see {@link passthrough} when types are identical * @see {@link passthroughSubtype} when `E extends T` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -563,6 +579,7 @@ export function passthroughSupertype(): Getter { * @see {@link passthrough} when types are identical * @see {@link passthroughSupertype} when `T extends E` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -599,6 +616,7 @@ export function passthroughSubtype(): Getter { * * @see {@link withDefault} to substitute a default for undefined values * + * @stability stable * @category validation * @since 4.0.0 */ @@ -642,6 +660,7 @@ export function required(annotations?: Schema.Annotations.Ke * @see {@link transform} when you need to change the value, not just validate * @see {@link fail} for unconditional failure * + * @stability stable * @category validation * @since 4.0.0 */ @@ -696,6 +715,7 @@ export function checkEffect( * @see {@link transformOptional} when you need to handle `None` inputs * @see {@link passthrough} when no transformation is needed * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -736,6 +756,7 @@ export function transform(f: (e: E) => T): Getter { * @see {@link transform} when transformation cannot fail * @see {@link transformOptionalEffect} when you need full `Option` control over the output * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -772,6 +793,7 @@ export function transformEffect( * @see {@link transform} when you only need to transform present values * @see {@link omit} when you always want `None` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -782,6 +804,7 @@ export function transformOptional(f: (oe: Option.Option) => Option.Opti /** * Creates a getter that effectfully transforms the full `Option`. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -819,6 +842,7 @@ export function transformOptionalEffect( * @see {@link transformOptional} when you want conditional omission * @see {@link forbidden} when you want to fail instead of silently omit * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -852,6 +876,7 @@ export function omit(): Getter { * @see {@link transformOptionalEffect} for custom effectful missing-key handling * @see {@link required} when absent input should fail instead of using a default * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -887,6 +912,7 @@ export function withDefault( * * @see {@link transform} for custom string conversions * + * @stability stable * @category converting * @since 4.0.0 */ @@ -918,6 +944,7 @@ export function String(): Getter { * * @see {@link transformEffect} for effectful or validated number parsing * + * @stability stable * @category converting * @since 4.0.0 */ @@ -946,6 +973,7 @@ export function Number(): Getter { * Effect.runSync(SchemaGetter.run(toBool, Option.some("true"), {})) // => Option.some(true) * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -975,6 +1003,7 @@ export function Boolean(): Getter { * Effect.runSync(SchemaGetter.run(toBigInt, Option.some("42"), {})) // => Option.some(42n) * ``` * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1007,6 +1036,7 @@ export function BigInt(): Getter(): Getter { * Effect.runSync(SchemaGetter.run(trimmed, Option.some(" hello "), {})) // => Option.some("hello") * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1053,6 +1084,7 @@ export function trim(): Getter { * Effect.runSync(SchemaGetter.run(cap, Option.some("hello"), {})) // => Option.some("Hello") * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1076,6 +1108,7 @@ export function capitalize(): Getter { * Effect.runSync(SchemaGetter.run(uncap, Option.some("Hello"), {})) // => Option.some("hello") * ``` * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1101,6 +1134,7 @@ export function uncapitalize(): Getter { * * @see {@link camelToSnake} for the inverse operation * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1126,6 +1160,7 @@ export function snakeToCamel(): Getter { * * @see {@link snakeToCamel} for the inverse operation * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1151,6 +1186,7 @@ export function camelToSnake(): Getter { * * @see {@link toUpperCase} for the inverse operation * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1176,6 +1212,7 @@ export function toLowerCase(): Getter { * * @see {@link toLowerCase} for the inverse operation * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1216,6 +1253,7 @@ type ParseJsonOptions = { * * @see {@link stringifyJson} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1238,6 +1276,7 @@ export function parseJson(options?: ParseJsonOptions | undefin /** * Replacer function or property allowlist accepted by `JSON.stringify`. * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1279,6 +1318,7 @@ type StringifyJsonOptions = { * * @see {@link parseJson} for the inverse operation * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1313,8 +1353,9 @@ export function stringifyJson(options?: StringifyJsonOptions): Getter(options?: { const keyValueSeparator = options?.keyValueSeparator ?? "=" return transform((input) => input.split(separator).reduce((acc, pair) => { - const [key, value] = pair.split(keyValueSeparator) - if (key && value) { + if (keyValueSeparator === "") { + const [key, value] = pair.split("") + if (key && value) InternalRecord.assignProperty(acc, key, value) + return acc + } + const index = pair.indexOf(keyValueSeparator) + if (index <= 0) return acc + const key = pair.slice(0, index) + const value = pair.slice(index + keyValueSeparator.length) + if (value) { InternalRecord.assignProperty(acc, key, value) } return acc @@ -1375,6 +1425,7 @@ export function splitKeyValue(options?: { * * @see {@link splitKeyValue} for the inverse operation * + * @stability stable * @category combining * @since 4.0.0 */ @@ -1414,6 +1465,7 @@ export function joinKeyValue>(options?: { * * @see {@link splitKeyValue} when values are key-value pairs * + * @stability stable * @category splitting * @since 4.0.0 */ @@ -1445,6 +1497,7 @@ export function split(options?: { * @see {@link decodeBase64String} for the inverse operation to `string` * @see {@link encodeBase64Url} for the URL-safe variant * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1473,6 +1526,7 @@ export function encodeBase64(): Getter * @see {@link decodeBase64UrlString} for the inverse operation to `string` * @see {@link encodeBase64} for the standard Base64 variant * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1500,6 +1554,7 @@ export function encodeBase64Url(): Getter(): Getter { * @see {@link decodeBase64String} to decode to `string` instead * @see {@link encodeBase64} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1564,6 +1620,7 @@ export function decodeBase64(): Getter { * @see {@link decodeBase64} to decode to `Uint8Array` instead * @see {@link encodeBase64} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1603,6 +1660,7 @@ export function decodeBase64String(): Getter { * @see {@link decodeBase64UrlString} to decode to `string` instead * @see {@link encodeBase64Url} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1642,6 +1700,7 @@ export function decodeBase64Url(): Getter { * @see {@link decodeBase64Url} to decode to `Uint8Array` instead * @see {@link encodeBase64Url} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1681,6 +1740,7 @@ export function decodeBase64UrlString(): Getter { * @see {@link decodeHexString} to decode to `string` instead * @see {@link encodeHex} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1720,6 +1780,7 @@ export function decodeHex(): Getter { * @see {@link decodeHex} to decode to `Uint8Array` instead * @see {@link encodeHex} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1760,6 +1821,7 @@ export function decodeHexString(): Getter { * * @see {@link decodeUriComponent} for the inverse operation * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1786,6 +1848,7 @@ export function encodeUriComponent(): Getter { * * @see {@link encodeUriComponent} for the inverse operation * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1836,6 +1899,7 @@ export function decodeUriComponent(): Getter { * * @see {@link Date} for a simpler coercion to `Date` (no validation) * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1881,6 +1945,7 @@ export function dateTimeUtcFromInput(): Gette * @see {@link makeTreeRecord} for the underlying bracket-path parser * @see {@link decodeURLSearchParams} for the URLSearchParams variant * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1920,6 +1985,7 @@ const collectFormDataEntries = collectBracketPathEntries((value): value is strin * @see {@link collectBracketPathEntries} for the underlying flattener * @see {@link encodeURLSearchParams} for the URLSearchParams variant * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1965,6 +2031,7 @@ export function encodeFormData(): Getter { * @see {@link makeTreeRecord} for the underlying bracket-path parser * @see {@link decodeFormData} for the FormData variant * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -2001,6 +2068,7 @@ const collectURLSearchParamsEntries = collectBracketPathEntries(Predicate.isStri * @see {@link collectBracketPathEntries} for the underlying flattener * @see {@link encodeFormData} for the FormData variant * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2074,6 +2142,7 @@ function bracketPathToTokens(bracketPath: string): Array { * @see {@link decodeFormData} for a higher-level FormData decoder * @see {@link decodeURLSearchParams} for a higher-level URLSearchParams decoder * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2172,6 +2241,7 @@ export function makeTreeRecord( * @see {@link encodeFormData} for a higher-level FormData encoder * @see {@link encodeURLSearchParams} for a higher-level URLSearchParams encoder * + * @stability stable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SchemaIssue.ts b/packages/effect/src/SchemaIssue.ts similarity index 98% rename from repos/effect/packages/effect/src/SchemaIssue.ts rename to packages/effect/src/SchemaIssue.ts index 07e95a426..6e32f0911 100644 --- a/repos/effect/packages/effect/src/SchemaIssue.ts +++ b/packages/effect/src/SchemaIssue.ts @@ -7,6 +7,7 @@ * keys, invalid types, invalid values, failed filters, failed transformations, * and alternatives that did not match. This module also formats issues. * + * @stability stable * @since 4.0.0 */ import * as Arr from "./Array.ts" @@ -45,6 +46,7 @@ const TypeId = "~effect/SchemaIssue/Issue" * * @see {@link Issue} * + * @stability stable * @category guards * @since 4.0.0 */ @@ -78,6 +80,7 @@ export function isIssue(u: unknown): u is Issue { * * @see {@link Issue} for the complete issue model * + * @stability stable * @category guards * @since 4.0.0 */ @@ -101,6 +104,7 @@ export function hasInput(issue: Issue): issue is Issue & { readonly input: unkno * @see {@link Issue} — the full union including composite nodes * @see {@link LeafHook} — formatter hook that operates on `Leaf` values * + * @stability stable * @category models * @since 4.0.0 */ @@ -136,6 +140,7 @@ export type Leaf = * @see {@link Leaf} — the terminal subset * @see {@link isIssue} — type guard * @see {@link hasInput} — checks whether an issue reports an input + * @stability stable * @category models * @since 4.0.0 */ @@ -208,6 +213,7 @@ class IssueNodeImpl implements IssueNode { * @see {@link Leaf} — terminal issue types that commonly appear as the inner `issue` * @see {@link CheckHook} — formatter hook for `Filter` issues * + * @stability stable * @category models * @since 4.0.0 */ @@ -226,6 +232,7 @@ export interface Filter extends IssueNode { /** * Constructs a schema issue for a failed refinement check. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -299,6 +306,7 @@ export const Filter: new( * @see {@link Filter} — failure from a refinement check (not a transformation) * @see {@link Composite} — multiple issues from a single schema node * + * @stability stable * @category models * @since 4.0.0 */ @@ -317,6 +325,7 @@ export interface Encoding extends IssueNode { /** * Constructs a schema issue for a failed transformation. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -378,6 +387,7 @@ export const Encoding: new( * * @see {@link Composite} — groups multiple issues under one schema node * + * @stability stable * @category models * @since 4.0.0 */ @@ -396,6 +406,7 @@ export interface Pointer extends IssueNode { /** * Constructs a schema issue that points to a nested location. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -440,6 +451,7 @@ export const Pointer: new(path: ReadonlyArray, issue: Issue) => Poi * @see {@link Pointer} — wraps this issue with the missing key's path * @see {@link UnexpectedKey} — the opposite case (extra key present) * + * @stability stable * @category models * @since 4.0.0 */ @@ -454,6 +466,7 @@ export interface MissingKey extends IssueNode { /** * Constructs a schema issue for a missing key or tuple index. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -496,6 +509,7 @@ export const MissingKey: new(annotations: Schema.Annotations.Key | unde * @see {@link MissingKey} — the opposite case (required key absent) * @see {@link Pointer} — wraps this issue with the unexpected key's path * + * @stability stable * @category models * @since 4.0.0 */ @@ -510,6 +524,7 @@ export interface UnexpectedKey extends IssueNode { /** * Constructs a schema issue for an unexpected key or tuple index. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -559,6 +574,7 @@ export const UnexpectedKey: new( * @see {@link AnyOf} — used for union no-match errors (similar but different semantics) * @see {@link Pointer} — adds path context to individual issues * + * @stability stable * @category models * @since 4.0.0 */ @@ -577,6 +593,7 @@ export interface Composite extends IssueNode { /** * Constructs a schema issue that groups multiple child issues. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -648,6 +665,7 @@ export const Composite: new( * * @see {@link InvalidValue} — the input has the right type but fails a value constraint * + * @stability stable * @category models * @since 4.0.0 */ @@ -662,6 +680,7 @@ export interface InvalidType extends IssueNode { /** * Constructs a schema issue for an input with an invalid runtime type. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -727,6 +746,7 @@ export const InvalidType: new( * @see {@link InvalidType} — the input has the wrong type entirely * @see {@link Filter} — composite wrapper when a schema filter produces this issue * + * @stability stable * @category models * @since 4.0.0 */ @@ -741,6 +761,7 @@ export interface InvalidValue extends IssueNode { /** * Constructs a schema issue for a value that violates a constraint. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -819,6 +840,7 @@ export function makeCompositeAtKey( * * @see {@link InvalidValue} — for value-constraint failures (not operation failures) * + * @stability stable * @category models * @since 4.0.0 */ @@ -833,6 +855,7 @@ export interface Forbidden extends IssueNode { /** * Constructs a schema issue for a forbidden parsing operation. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -889,6 +912,7 @@ export const Forbidden: new( * @see {@link OneOf} — the opposite: *too many* members matched * @see {@link Composite} — groups multiple issues under a non-union schema * + * @stability stable * @category models * @since 4.0.0 */ @@ -907,6 +931,7 @@ export interface AnyOf extends IssueNode { /** * Constructs a schema issue for a value that matches no union member. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -971,6 +996,7 @@ export const AnyOf: new( * * @see {@link AnyOf} — the opposite: *no* members matched * + * @stability stable * @category models * @since 4.0.0 */ @@ -989,6 +1015,7 @@ export interface OneOf extends IssueNode { /** * Constructs a schema issue for a value that matches multiple union members. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1091,6 +1118,7 @@ export function normalizeFilterOutput( * @see {@link makeFormatterDefault} — creates a `Formatter` * @see {@link makeFormatterStandardSchemaV1} — creates a `Formatter` * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1107,6 +1135,7 @@ export interface Formatter extends FormatterI {} * @see {@link defaultLeafHook} — the built-in implementation * @see {@link Leaf} — the union of terminal issue types * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1149,6 +1178,7 @@ export type LeafHook = (issue: Leaf) => string * @see {@link LeafHook} * @see {@link makeFormatterStandardSchemaV1} * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1197,6 +1227,7 @@ export const defaultLeafHook: LeafHook = (issue): string => { * @see {@link defaultCheckHook} — the built-in implementation * @see {@link Filter} — the issue type this hook formats * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1220,6 +1251,7 @@ export type CheckHook = (issue: Filter) => string | undefined * @see {@link CheckHook} * @see {@link makeFormatterStandardSchemaV1} * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1264,6 +1296,7 @@ export const defaultCheckHook: CheckHook = (issue): string | undefined => findMe * @see {@link LeafHook} * @see {@link CheckHook} * + * @stability stable * @category formatting * @since 4.0.0 */ @@ -1387,6 +1420,7 @@ function formatCheck(check: SchemaAST.Check): string { * @see {@link makeFormatterStandardSchemaV1} — produces Standard Schema V1 format instead * @see {@link Formatter} * + * @stability stable * @category formatting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SchemaParser.ts b/packages/effect/src/SchemaParser.ts similarity index 98% rename from repos/effect/packages/effect/src/SchemaParser.ts rename to packages/effect/src/SchemaParser.ts index 0ae10b550..f590b6e14 100644 --- a/repos/effect/packages/effect/src/SchemaParser.ts +++ b/packages/effect/src/SchemaParser.ts @@ -8,6 +8,7 @@ * synchronous functions that throw. It also contains the lower-level runner that * walks a schema AST and reports schema failures as `SchemaIssue.Issue` values. * + * @stability stable * @since 4.0.0 */ import * as Cause from "./Cause.ts" @@ -37,6 +38,7 @@ import * as SchemaIssue from "./SchemaIssue.ts" * runs type-side validation unless checks are disabled, and fails with a * `SchemaIssue.Issue` when construction fails. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -68,6 +70,7 @@ export function makeEffect(schema: S) { * Causes that contain defects, interruptions, or asynchronous work at this * synchronous boundary throw an `Error` whose cause is the underlying `Cause`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -105,6 +108,7 @@ export function makeOption(schema: S) { * synchronous boundary throw an `Error` whose cause is the underlying `Cause`, * instead of being converted to a schema validation error. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -132,6 +136,7 @@ export function make(schema: S) { * that contain defects, interruptions, or asynchronous work at this synchronous * boundary throw an `Error` whose cause is the underlying `Cause`. * + * @stability stable * @category guards * @since 3.10.0 */ @@ -223,6 +228,7 @@ export function _issue(ast: SchemaAST.AST) { * synchronous boundary throw an `Error` whose cause is the underlying `Cause`, * instead of being converted to a schema validation error. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -257,6 +263,7 @@ export function asserts(schema: S, input: I): as * * @see {@link decodeEffect} for input already typed as the schema's `Encoded` type * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -292,6 +299,7 @@ export function decodeUnknownEffect( * @see {@link decodeUnknownEffect} for untyped boundary input * @see {@link encodeEffect} for the opposite direction * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -327,6 +335,7 @@ export const decodeEffect: ( * @see {@link decodePromise} for input already typed as the schema's `Encoded` type * @see {@link decodeUnknownEffect} for schemas that require decoding services or when failures should remain in `Effect` * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -362,6 +371,7 @@ export function decodeUnknownPromise * @see {@link decodeUnknownPromise} for untyped input returning a JavaScript `Promise` * @see {@link decodeEffect} for preserving decoding services and failures in `Effect` * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -398,6 +408,7 @@ export function decodePromise>( * @see {@link decodeUnknownResult} for returning schema issues as data * @see {@link decodeUnknownSync} for throwing on decoding failure * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -431,6 +442,7 @@ export function decodeUnknownExit>( * @see {@link decodeUnknownExit} for untyped input with the same `Exit` result shape * @see {@link decodeEffect} for preserving decoding services and failures in `Effect` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -477,6 +489,7 @@ export const decodeOption: >( * @see {@link decodeResult} for input already typed as the schema's `Encoded` type * @see {@link decodeUnknownEffect} for effectful or service-requiring decoding * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -510,6 +523,7 @@ export function decodeUnknownResult> * @see {@link decodeUnknownResult} for untyped input with the same `Result` shape * @see {@link decodeEffect} for effectful or service-requiring decoding * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -545,6 +559,7 @@ export const decodeResult: >( * @see {@link decodeUnknownEffect} for preserving decoding failures in `Effect` * @see {@link decodeUnknownResult} for returning schema issues as data * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -584,6 +599,7 @@ export function decodeUnknownSync>( * @see {@link decodeResult} for returning schema issues as data * @see {@link decodeEffect} for preserving decoding failures in `Effect` * + * @stability stable * @category decoding * @since 3.10.0 */ @@ -610,6 +626,7 @@ export const decodeSync: >( * * @see {@link encodeEffect} for the typed-input variant when the value is already typed as the schema's decoded `Type` * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -644,6 +661,7 @@ export function encodeUnknownEffect( * * @see {@link encodeUnknownEffect} for encoding unknown input before the value is statically typed as the schema's `Type` * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -679,6 +697,7 @@ export const encodeEffect: ( * @see {@link encodePromise} for input already typed as the schema's decoded `Type` * @see {@link encodeUnknownEffect} for schemas that require encoding services or when failures should remain in `Effect` * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -713,6 +732,7 @@ export const encodeUnknownPromise = * @see {@link encodeUnknownPromise} for encoding untyped input * @see {@link encodeEffect} for effectful encoding or schemas with encoding service requirements * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -744,6 +764,7 @@ export const encodePromise: >( * @see {@link encodeExit} for input already typed as the schema's decoded `Type` * @see {@link encodeUnknownEffect} for effectful encoding that preserves service requirements * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -777,6 +798,7 @@ export function encodeUnknownExit>( * @see {@link encodeUnknownExit} for unknown input with the same `Exit` result shape * @see {@link encodeEffect} for effectful encoding that preserves service requirements * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -825,6 +847,7 @@ export const encodeOption: >( * @see {@link encodeResult} for input already typed as the schema's decoded `Type` * @see {@link encodeUnknownEffect} for effectful encoding, including schemas with encoding service requirements * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -858,6 +881,7 @@ export function encodeUnknownResult> * * @see {@link encodeUnknownResult} for the same `Result` shape when the input is not already typed * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -892,6 +916,7 @@ export const encodeResult: >( * @see {@link encodeSync} for input already typed as the schema's decoded `Type` * @see {@link encodeUnknownEffect} for effectful encoding that preserves service requirements * + * @stability stable * @category encoding * @since 3.10.0 */ @@ -931,6 +956,7 @@ export function encodeUnknownSync>( * @see {@link encodeResult} for returning schema issues as data * @see {@link encodeEffect} for effectful encoding that preserves service requirements * + * @stability stable * @category encoding * @since 3.10.0 */ diff --git a/repos/effect/packages/effect/src/SchemaRepresentation.ts b/packages/effect/src/SchemaRepresentation.ts similarity index 96% rename from repos/effect/packages/effect/src/SchemaRepresentation.ts rename to packages/effect/src/SchemaRepresentation.ts index 42387557e..d6153da6e 100644 --- a/repos/effect/packages/effect/src/SchemaRepresentation.ts +++ b/packages/effect/src/SchemaRepresentation.ts @@ -1,6 +1,7 @@ /** * Open, compiler-extensible representation of Effect schemas. * + * @stability stable * @since 4.0.0 */ import * as InternalRecord from "./internal/record.ts" @@ -18,6 +19,7 @@ import * as InternalGetter from "./SchemaGetter.ts" /** * Open persistence identity carried by declarations and opaque checks. * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -29,6 +31,7 @@ export interface RepresentationAnnotation { /** * Open persistence identity and schema dependencies carried by opaque checks. * + * @stability stable * @category annotations * @since 4.0.0 */ @@ -39,6 +42,7 @@ export interface CheckRepresentationAnnotation extends RepresentationAnnotati /** * Input and output contracts for JSON Schema compiler annotations. * + * @stability stable * @since 4.0.0 */ export declare namespace ToJsonSchema { @@ -123,6 +127,7 @@ export declare namespace ToJsonSchema { /** * Input and output contracts for code generation annotations. * + * @stability stable * @since 4.0.0 */ export declare namespace Generation { @@ -189,6 +194,7 @@ export declare namespace Generation { /** * A custom opaque declaration. * + * @stability stable * @category models * @since 4.0.0 */ @@ -203,6 +209,7 @@ export interface Declaration { /** * A lazily resolved representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -216,6 +223,7 @@ export interface Suspend { /** * A named reference. * + * @stability stable * @category models * @since 4.0.0 */ @@ -233,6 +241,7 @@ interface Keyword { /** * The null keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -240,6 +249,7 @@ export interface Null extends Keyword<"Null"> {} /** * The undefined keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -247,6 +257,7 @@ export interface Undefined extends Keyword<"Undefined"> {} /** * The void keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -254,6 +265,7 @@ export interface Void extends Keyword<"Void"> {} /** * The never keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -261,6 +273,7 @@ export interface Never extends Keyword<"Never"> {} /** * The unknown keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -268,6 +281,7 @@ export interface Unknown extends Keyword<"Unknown"> {} /** * The any keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -276,6 +290,7 @@ export interface Any extends Keyword<"Any"> {} /** * A string representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -284,6 +299,7 @@ export interface String extends Keyword<"String"> {} /** * A number representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -291,6 +307,7 @@ export interface Number extends Keyword<"Number"> {} /** * A boolean representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -298,6 +315,7 @@ export interface Boolean extends Keyword<"Boolean"> {} /** * A bigint representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -305,6 +323,7 @@ export interface BigInt extends Keyword<"BigInt"> {} /** * A symbol representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -318,6 +337,7 @@ export interface Symbol extends Keyword<"Symbol"> {} * The live representation stores the native literal value. Persistent codecs * add an explicit type discriminator when encoding it. * + * @stability stable * @category models * @since 4.0.0 */ @@ -333,6 +353,7 @@ export interface Literal extends Keyword<"Literal"> { * Globally registered symbols have an exact JSON string representation. Local * symbols have no JSON representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -343,6 +364,7 @@ export interface UniqueSymbol extends Keyword<"UniqueSymbol"> { /** * The object keyword representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -356,6 +378,7 @@ export interface ObjectKeyword extends Keyword<"ObjectKeyword"> {} * Enum members are stored as native string or finite number values. Persistent * codecs add an explicit type discriminator when encoding them. * + * @stability stable * @category models * @since 4.0.0 */ @@ -366,6 +389,7 @@ export interface Enum extends Keyword<"Enum"> { /** * A template literal representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -376,6 +400,7 @@ export interface TemplateLiteral extends Keyword<"TemplateLiteral"> { /** * A tuple element. * + * @stability stable * @category models * @since 4.0.0 */ @@ -388,6 +413,7 @@ export interface Element { /** * An array or tuple representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -409,6 +435,7 @@ export interface Arrays extends Keyword<"Arrays"> { * Local symbols can be represented while the schema is live, but persistent * codecs reject them because they cannot be reconstructed by identity. * + * @stability stable * @category models * @since 4.0.0 */ @@ -423,6 +450,7 @@ export interface PropertySignature { /** * An index signature. * + * @stability stable * @category models * @since 4.0.0 */ @@ -434,6 +462,7 @@ export interface IndexSignature { /** * An object representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -445,6 +474,7 @@ export interface Objects extends Keyword<"Objects"> { /** * A union representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -456,6 +486,7 @@ export interface Union extends Keyword<"Union"> { /** * The structural schema representation. * + * @stability stable * @category models * @since 4.0.0 */ @@ -486,6 +517,7 @@ export type Representation = /** * A structural check. * + * @stability stable * @category models * @since 4.0.0 */ @@ -494,6 +526,7 @@ export type Check = Filter | FilterGroup /** * An opaque leaf check. * + * @stability stable * @category models * @since 4.0.0 */ @@ -507,6 +540,7 @@ export interface Filter { /** * A non-empty group of checks. * + * @stability stable * @category models * @since 4.0.0 */ @@ -520,6 +554,7 @@ export interface FilterGroup { /** * Named representation definitions. * + * @stability stable * @category models * @since 4.0.0 */ @@ -530,6 +565,7 @@ export interface References { /** * A single representation and its definitions. * + * @stability stable * @category models * @since 4.0.0 */ @@ -541,6 +577,7 @@ export interface Document { /** * Multiple representations sharing definitions. * + * @stability stable * @category models * @since 4.0.0 */ @@ -552,6 +589,7 @@ export interface MultiDocument { /** * Reviver for a declaration. * + * @stability stable * @category models * @since 4.0.0 */ @@ -568,6 +606,7 @@ export interface DeclarationReviver

{ /** * Reviver for a leaf check. * + * @stability stable * @category models * @since 4.0.0 */ @@ -584,6 +623,7 @@ export interface FilterReviver

{ /** * Reviver for a check group. * + * @stability stable * @category models * @since 4.0.0 */ @@ -600,6 +640,7 @@ export interface FilterGroupReviver

{ /** * A check reviver. * + * @stability stable * @category models * @since 4.0.0 */ @@ -608,6 +649,7 @@ export type CheckReviver

= FilterReviver

| FilterGroupReviver

/** * A typed reviver. * + * @stability stable * @category models * @since 4.0.0 */ @@ -616,6 +658,7 @@ export type Reviver

= DeclarationReviver

| CheckReviver

/** * A reviver erased only at collection boundaries. * + * @stability stable * @category models * @since 4.0.0 */ @@ -624,6 +667,7 @@ export type AnyReviver = Reviver /** * Creates a declaration reviver while inferring its payload type from `payloadSchema`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -636,6 +680,7 @@ export const makeReviverDeclaration:

( /** * Creates a filter reviver while inferring its payload type from `payloadSchema`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -648,6 +693,7 @@ export const makeReviverFilter:

( /** * Creates a filter group reviver while inferring its payload type from `payloadSchema`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -728,6 +774,7 @@ const RedactedRepresentationPayload: Schema.Decoder = makeReviverFilter( * * @see {@link Schema.isPattern} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -767,6 +815,7 @@ export const isPatternReviver: FilterReviver<{ * * @see {@link Schema.isStringFinite} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -785,6 +834,7 @@ export const isStringFiniteReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isStringBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -803,6 +853,7 @@ export const isStringBigIntReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isStringSymbol} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -821,6 +872,7 @@ export const isStringSymbolReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isUUID} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -841,6 +893,7 @@ export const isUUIDReviver: FilterReviver<{ * * @see {@link Schema.isGUID} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -859,6 +912,7 @@ export const isGUIDReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isULID} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -877,6 +931,7 @@ export const isULIDReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isBase64} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -895,6 +950,7 @@ export const isBase64Reviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isBase64Url} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -913,6 +969,7 @@ export const isBase64UrlReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isStartingWith} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -933,6 +990,7 @@ export const isStartingWithReviver: FilterReviver<{ * * @see {@link Schema.isEndingWith} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -953,6 +1011,7 @@ export const isEndingWithReviver: FilterReviver<{ * * @see {@link Schema.isIncluding} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -973,6 +1032,7 @@ export const isIncludingReviver: FilterReviver<{ * * @see {@link Schema.isUppercased} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -991,6 +1051,7 @@ export const isUppercasedReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isLowercased} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1009,6 +1070,7 @@ export const isLowercasedReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isCapitalized} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1027,6 +1089,7 @@ export const isCapitalizedReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isUncapitalized} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1045,6 +1108,7 @@ export const isUncapitalizedReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isFinite} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1063,6 +1127,7 @@ export const isFiniteReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isGreaterThan} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1083,6 +1148,7 @@ export const isGreaterThanReviver: FilterReviver<{ * * @see {@link Schema.isGreaterThanOrEqualTo} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1103,6 +1169,7 @@ export const isGreaterThanOrEqualToReviver: FilterReviver<{ * * @see {@link Schema.isLessThan} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1123,6 +1190,7 @@ export const isLessThanReviver: FilterReviver<{ * * @see {@link Schema.isLessThanOrEqualTo} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1143,6 +1211,7 @@ export const isLessThanOrEqualToReviver: FilterReviver<{ * * @see {@link Schema.isBetween} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1171,6 +1240,7 @@ export const isBetweenReviver: FilterReviver<{ * * @see {@link Schema.isMultipleOf} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1191,6 +1261,7 @@ export const isMultipleOfReviver: FilterReviver<{ * * @see {@link Schema.isInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1209,6 +1280,7 @@ export const isIntReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isMinLength} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1229,6 +1301,7 @@ export const isMinLengthReviver: FilterReviver<{ * * @see {@link Schema.isMaxLength} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1249,6 +1322,7 @@ export const isMaxLengthReviver: FilterReviver<{ * * @see {@link Schema.isBetweenLength} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1265,6 +1339,7 @@ export const isBetweenLengthReviver: FilterReviver<{ * Reviver for persisted `isMinCodePoints` checks. * * @see {@link Schema.isMinCodePoints} for creating the corresponding check + * @stability stable * @category validation * @since 4.0.0 */ @@ -1280,6 +1355,7 @@ export const isMinCodePointsReviver: FilterReviver<{ * Reviver for persisted `isMaxCodePoints` checks. * * @see {@link Schema.isMaxCodePoints} for creating the corresponding check + * @stability stable * @category validation * @since 4.0.0 */ @@ -1295,6 +1371,7 @@ export const isMaxCodePointsReviver: FilterReviver<{ * Reviver for persisted `isBetweenCodePoints` checks. * * @see {@link Schema.isBetweenCodePoints} for creating the corresponding check + * @stability stable * @category validation * @since 4.0.0 */ @@ -1316,6 +1393,7 @@ export const isBetweenCodePointsReviver: FilterReviver<{ * * @see {@link Schema.isMinSize} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1336,6 +1414,7 @@ export const isMinSizeReviver: FilterReviver<{ * * @see {@link Schema.isMaxSize} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1356,6 +1435,7 @@ export const isMaxSizeReviver: FilterReviver<{ * * @see {@link Schema.isBetweenSize} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1377,6 +1457,7 @@ export const isBetweenSizeReviver: FilterReviver<{ * * @see {@link Schema.isMinProperties} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1397,6 +1478,7 @@ export const isMinPropertiesReviver: FilterReviver<{ * * @see {@link Schema.isMaxProperties} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1417,6 +1499,7 @@ export const isMaxPropertiesReviver: FilterReviver<{ * * @see {@link Schema.isBetweenProperties} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1438,6 +1521,7 @@ export const isBetweenPropertiesReviver: FilterReviver<{ * * @see {@link Schema.isPropertyNames} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1456,6 +1540,7 @@ export const isPropertyNamesReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.isUnique} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -1473,6 +1558,7 @@ export const isUniqueReviver: FilterReviver = makeReviverFilter( * Use when reconstructing documents that may contain checks created by {@link Schema.isUniqueKey}. * * @see {@link Schema.isUniqueKey} for creating the corresponding check + * @stability stable * @category validation * @since 4.0.0 */ @@ -1491,6 +1577,7 @@ export const isUniqueKeyReviver: FilterReviver = makeReviverFilter( * * @see {@link Schema.Option} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1512,6 +1599,7 @@ export const OptionReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.Result} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1533,6 +1621,7 @@ export const ResultReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.Redacted} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1554,6 +1643,7 @@ export const RedactedReviver: DeclarationReviver * * @see {@link Schema.CauseReason} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1575,6 +1665,7 @@ export const CauseReasonReviver: DeclarationReviver = makeReviverDeclarati * * @see {@link Schema.Cause} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1596,6 +1687,7 @@ export const CauseReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.ErrorInstance} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1617,6 +1709,7 @@ export const ErrorInstanceReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.ReadonlyMap} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1653,6 +1747,7 @@ export const ReadonlyMapReviver: DeclarationReviver = makeReviverDeclarati /** * Reviver for persisted {@link Schema.Graph} declarations. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1674,6 +1769,7 @@ export const GraphReviver: DeclarationReviver<"directed" | "undirected"> = makeR * * @see {@link Schema.HashMap} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1695,6 +1791,7 @@ export const HashMapReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.ReadonlySet} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1716,6 +1813,7 @@ export const ReadonlySetReviver: DeclarationReviver = makeReviverDeclarati * * @see {@link Schema.HashSet} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1737,6 +1835,7 @@ export const HashSetReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.Chunk} for creating the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1758,6 +1857,7 @@ export const ChunkReviver: DeclarationReviver = makeReviverDeclaration( * * @see {@link Schema.RegExp} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1775,6 +1875,7 @@ export const RegExpReviver: DeclarationReviver = makeFixedDeclarationReviv * * @see {@link Schema.URL} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1789,6 +1890,7 @@ export const URLReviver: DeclarationReviver = makeFixedDeclarationReviver( * * @see {@link Schema.Date} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1803,6 +1905,7 @@ export const DateReviver: DeclarationReviver = makeFixedDeclarationReviver * * @see {@link Schema.Duration} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1814,6 +1917,7 @@ export const DurationReviver: DeclarationReviver = makeFixedDeclarationRev /** * Reviver for persisted {@link Schema.ByteSize} declarations. * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1831,6 +1935,7 @@ export const ByteSizeReviver: DeclarationReviver = makeFixedDeclarationRev * * @see {@link Schema.BigDecimal} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1848,6 +1953,7 @@ export const BigDecimalReviver: DeclarationReviver = makeFixedDeclarationR * * @see {@link Schema.File} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1862,6 +1968,7 @@ export const FileReviver: DeclarationReviver = makeFixedDeclarationReviver * * @see {@link Schema.FormData} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1879,6 +1986,7 @@ export const FormDataReviver: DeclarationReviver = makeFixedDeclarationRev * * @see {@link Schema.URLSearchParams} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1896,6 +2004,7 @@ export const URLSearchParamsReviver: DeclarationReviver = makeFixedDeclara * * @see {@link Schema.Uint8Array} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1913,6 +2022,7 @@ export const Uint8ArrayReviver: DeclarationReviver = makeFixedDeclarationR * * @see {@link Schema.DateTimeUtc} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1930,6 +2040,7 @@ export const DateTimeUtcReviver: DeclarationReviver = makeFixedDeclaration * * @see {@link Schema.TimeZoneOffset} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1947,6 +2058,7 @@ export const TimeZoneOffsetReviver: DeclarationReviver = makeFixedDeclarat * * @see {@link Schema.TimeZoneNamed} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1964,6 +2076,7 @@ export const TimeZoneNamedReviver: DeclarationReviver = makeFixedDeclarati * * @see {@link Schema.TimeZone} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1981,6 +2094,7 @@ export const TimeZoneReviver: DeclarationReviver = makeFixedDeclarationRev * * @see {@link Schema.DateTimeZoned} for the corresponding schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -1998,6 +2112,7 @@ export const DateTimeZonedReviver: DeclarationReviver = makeFixedDeclarati * * @see {@link Schema.isGreaterThanDate} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2018,6 +2133,7 @@ export const isGreaterThanDateReviver: FilterReviver<{ * * @see {@link Schema.isGreaterThanOrEqualToDate} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2038,6 +2154,7 @@ export const isGreaterThanOrEqualToDateReviver: FilterReviver<{ * * @see {@link Schema.isLessThanDate} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2058,6 +2175,7 @@ export const isLessThanDateReviver: FilterReviver<{ * * @see {@link Schema.isLessThanOrEqualToDate} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2078,6 +2196,7 @@ export const isLessThanOrEqualToDateReviver: FilterReviver<{ * * @see {@link Schema.isBetweenDate} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2106,6 +2225,7 @@ export const isBetweenDateReviver: FilterReviver<{ * * @see {@link Schema.isGreaterThanBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2126,6 +2246,7 @@ export const isGreaterThanBigIntReviver: FilterReviver<{ * * @see {@link Schema.isGreaterThanOrEqualToBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2146,6 +2267,7 @@ export const isGreaterThanOrEqualToBigIntReviver: FilterReviver<{ * * @see {@link Schema.isLessThanBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2166,6 +2288,7 @@ export const isLessThanBigIntReviver: FilterReviver<{ * * @see {@link Schema.isLessThanOrEqualToBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2186,6 +2309,7 @@ export const isLessThanOrEqualToBigIntReviver: FilterReviver<{ * * @see {@link Schema.isBetweenBigInt} for creating the corresponding check * + * @stability stable * @category validation * @since 4.0.0 */ @@ -2214,6 +2338,7 @@ export const isBetweenBigIntReviver: FilterReviver<{ * * @see {@link Schema.Json} for the corresponding immutable JSON schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -2228,6 +2353,7 @@ export const JsonReviver: DeclarationReviver = makeFixedDeclarationReviver * * @see {@link Schema.MutableJson} for the corresponding mutable JSON schema * + * @stability stable * @category schemas * @since 4.0.0 */ @@ -2284,6 +2410,7 @@ const jsonSchemaRevivers: ReadonlyArray = [ * `onEnter` must return a JSON Schema object. Its result is used directly, and exceptions raised by the callback pass * through unchanged. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2300,6 +2427,7 @@ export interface FromJsonSchemaOptions { /** * Runtime and TypeScript source generated for one schema. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2311,6 +2439,7 @@ export interface Code { /** * Creates generated runtime and TypeScript source strings for a schema. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2319,6 +2448,7 @@ export const makeCode: (runtime: string, Type: string) => Code = InternalToCodeD /** * Auxiliary source artifact emitted while generating schema code. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2341,6 +2471,7 @@ export type Artifact = /** * Generated schema code together with named references and auxiliary artifacts. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2359,6 +2490,7 @@ export interface CodeDocument { /** * Information supplied to a reference policy for one representation candidate. * + * @stability stable * @category models * @since 4.0.0 */ @@ -2393,6 +2525,7 @@ export interface ReferencePolicyInput { * * @see {@link ToRepresentationOptions} for configuring representation generation * + * @stability stable * @category models * @since 4.0.0 */ @@ -2401,6 +2534,7 @@ export type ReferencePolicy = (input: ReferencePolicyInput) => string | undefine /** * Options for generating schema representations. * + * @stability stable * @category configuration * @since 4.0.0 */ @@ -2438,6 +2572,7 @@ export interface ToRepresentationOptions { * * @see {@link toRepresentations} for multiple roots sharing one reference table * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2459,6 +2594,7 @@ export function toRepresentation(ast: SchemaAST.AST, options?: ToRepresentationO * * @see {@link toRepresentation} for a single AST root * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -2476,6 +2612,7 @@ export function toRepresentations( * * Use when an API such as `toCodeDocument` requires a `MultiDocument`. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2526,6 +2663,7 @@ export function toMultiDocument(document: Document): MultiDocument { * * @see {@link toJsonSchemaMultiDocument} for multiple roots sharing definitions * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2561,6 +2699,7 @@ export function toJsonSchemaDocument( * * @see {@link toJsonSchemaDocument} for a single root * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2582,6 +2721,7 @@ export function toJsonSchemaMultiDocument( * * Opaque declarations and leaf checks require `toCode` callbacks. Callback results are used directly, and exceptions raised by a callback pass through unchanged. * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2812,6 +2952,7 @@ const decodeMultiDocument = Schema.decodeSync(MultiDocumentFromJson) * @see {@link toRepresentation} for constructing the live document * @see {@link toJsonMultiDocument} for documents with multiple roots * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2833,6 +2974,7 @@ export function toJson(document: Document): Schema.Json { * @see {@link toRepresentations} for constructing the live multi-document * @see {@link toJson} for a single-root document * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -2855,6 +2997,7 @@ export function toJsonMultiDocument(document: MultiDocument): Schema.Json { * @see {@link fromRepresentation} for reconstructing a runtime schema * @see {@link fromJsonMultiDocument} for multiple roots sharing references * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -2877,6 +3020,7 @@ export function fromJson(input: Schema.Json): Document { * @see {@link fromRepresentations} for reconstructing runtime schemas * @see {@link fromJson} for a single root * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -2912,6 +3056,7 @@ export function fromJsonMultiDocument(input: Schema.Json): MultiDocument { * @see {@link fromJson} for decoding a persisted document * @see {@link fromRepresentations} for multiple roots sharing references * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2936,6 +3081,7 @@ export function fromRepresentation( * @see {@link fromJsonMultiDocument} for decoding a persisted multi-document * @see {@link fromRepresentation} for a single root * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2997,6 +3143,7 @@ export function fromRepresentations( * @see {@link fromJsonSchemaMultiDocument} for multiple roots sharing definitions * @see {@link toRepresentation} for converting the result to a representation document * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -3040,6 +3187,7 @@ export function fromJsonSchemaDocument( * @see {@link fromJsonSchemaDocument} for a single root * @see {@link toRepresentations} for converting the returned schema ASTs to a representation document * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SchemaTransformation.ts b/packages/effect/src/SchemaTransformation.ts similarity index 98% rename from repos/effect/packages/effect/src/SchemaTransformation.ts rename to packages/effect/src/SchemaTransformation.ts index d02d894a8..7824aa0e3 100644 --- a/repos/effect/packages/effect/src/SchemaTransformation.ts +++ b/packages/effect/src/SchemaTransformation.ts @@ -9,6 +9,7 @@ * constructors for pure or effectful conversions, and common conversions used * by the Schema module. * + * @stability stable * @since 4.0.0 */ @@ -69,6 +70,7 @@ import * as SchemaIssue from "./SchemaIssue.ts" * * @see {@link Transformation} — value-level bidirectional transformation * + * @stability stable * @category models * @since 4.0.0 */ @@ -88,6 +90,7 @@ export interface Middleware extends Pipe /** * Constructs schema middleware from its decode and encode functions. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -172,6 +175,7 @@ const TypeId = "~effect/SchemaTransformation/Transformation" * @see {@link transformEffect} — construct from effectful functions * @see {@link Middleware} — effect-pipeline-level alternative * + * @stability stable * @category models * @since 4.0.0 */ @@ -186,6 +190,7 @@ export interface Transformation exte /** * Constructs a bidirectional schema transformation from its decode and encode getters. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -239,6 +244,7 @@ export const Transformation: new( * Schema.decodeUnknownSync(schema)(" HELLO ") // => "hello" * ``` * + * @stability stable * @category combining * @since 4.0.0 */ @@ -284,6 +290,7 @@ export const composeTransformation: { * @see {@link Transformation} * @see {@link makeTransformation} * + * @stability stable * @category guards * @since 4.0.0 */ @@ -321,6 +328,7 @@ export function isTransformation(u: unknown): u is Transformation(options: { * @see {@link transformOptional} — for transformations that handle missing keys * @see {@link makeTransformation} — for transformations from existing Getters * + * @stability stable * @category transforming * @since 3.10.0 */ @@ -423,6 +432,7 @@ export function transformEffect(options: { * @see {@link transformOptional} — for transformations that handle missing keys * @see {@link passthrough} — when no conversion is needed * + * @stability stable * @category transforming * @since 3.10.0 */ @@ -476,6 +486,7 @@ export function transform(options: { * @see {@link optionFromOptionalKey} — built-in for the common optional-key-to-Option pattern * @see {@link optionFromOptional} — built-in for optional (undefined) to Option * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -519,6 +530,7 @@ export function transformOptional(options: { * @see {@link toUpperCase} * @see {@link snakeToCamel} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -558,6 +570,7 @@ export function trim(): Transformation { * @see {@link trim} * @see {@link toLowerCase} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -596,6 +609,7 @@ export function snakeToCamel(): Transformation { * @see {@link toUpperCase} * @see {@link trim} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -634,6 +648,7 @@ export function toLowerCase(): Transformation { * @see {@link toLowerCase} * @see {@link trim} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -672,6 +687,7 @@ export function toUpperCase(): Transformation { * @see {@link uncapitalize} * @see {@link toUpperCase} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -710,6 +726,7 @@ export function capitalize(): Transformation { * @see {@link capitalize} * @see {@link toLowerCase} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -753,6 +770,7 @@ export function uncapitalize(): Transformation { * @see {@link trim} * @see {@link snakeToCamel} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -802,6 +820,7 @@ const passthrough_ = new Transformation( * @see {@link passthroughSubtype} * @see {@link transform} * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -837,6 +856,7 @@ export function passthrough(): Transformation { * @see {@link passthrough} * @see {@link passthroughSubtype} * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -871,6 +891,7 @@ export function passthroughSupertype(): Transformation { * @see {@link passthrough} * @see {@link passthroughSupertype} * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -909,6 +930,7 @@ export function passthroughSubtype(): Transformation { * @see {@link bigintFromString} * @see {@link transform} * + * @stability stable * @category converting * @since 4.0.0 */ @@ -946,6 +968,7 @@ export const numberFromString = new Transformation( * @see {@link numberFromString} * @see {@link transform} * + * @stability stable * @category converting * @since 4.0.0 */ @@ -982,6 +1005,7 @@ export const bigintFromString = new Transformation( * @see {@link dateFromMillis} * @see {@link dateTimeUtcFromString} * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1023,6 +1047,7 @@ export const dateFromString: Transformation = new Trans * @see {@link dateFromString} * @see {@link SchemaGetter.dateTimeUtcFromInput} * + * @stability stable * @category converting * @since 4.0.0 */ @@ -1061,6 +1086,7 @@ export const dateFromMillis: Transformation = new Trans * @see {@link durationFromNanos} * @see {@link durationFromMillis} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1111,6 +1137,7 @@ export const durationFromString: Transformation = tra * * @see {@link durationFromMillis} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1157,6 +1184,7 @@ export const durationFromNanos: Transformation = tran * * @see {@link durationFromNanos} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1168,6 +1196,7 @@ export const durationFromMillis: Transformation = tra /** * Decodes a string into a `ByteSize` and encodes it as an exact string. * + * @stability unstable * @category transforming * @since 4.0.0 */ @@ -1190,6 +1219,7 @@ export const byteSizeFromString: Transformation = tra /** * Decodes a non-negative bigint byte count into a `ByteSize`. * + * @stability unstable * @category transforming * @since 4.0.0 */ @@ -1212,6 +1242,7 @@ export const byteSizeFromBigInt: Transformation = tra /** * Decodes a non-negative safe-integer byte count into a `ByteSize`. * + * @stability unstable * @category transforming * @since 4.0.0 */ @@ -1353,6 +1384,7 @@ export const defectFromJson = (options?: ErrorOptions) => * * @see {@link optionFromNullishOr} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1395,6 +1427,7 @@ export function optionFromNullOr(): Transformation, T | null * @see {@link optionFromOptionalKey} * @see {@link optionFromOptional} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1439,6 +1472,7 @@ export function optionFromUndefinedOr(): Transformation, T | * @see {@link optionFromNullOr} * @see {@link optionFromUndefinedOr} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1489,6 +1523,7 @@ export function optionFromNullishOr( * @see {@link optionFromUndefinedOr} * @see {@link transformOptional} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1535,6 +1570,7 @@ export function optionFromOptionalKey(): Transformation, T> * @see {@link optionFromUndefinedOr} * @see {@link transformOptional} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1573,6 +1609,7 @@ export function optionFromOptional(): Transformation, T | un * @see {@link numberFromString} * @see {@link transformEffect} * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1605,6 +1642,7 @@ export const urlFromString: Transformation = transformEffect * @see {@link fromJsonString} * @see `Schema.Uint8ArrayFromBase64` - a ready-made schema wrapping this transformation. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1691,6 +1730,7 @@ export const uint8ArrayFromBase64String: Transformation = new Transf * @see {@link stringFromBase64String} * @see `Schema.StringFromBase64Url` - a ready-made schema wrapping this transformation. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1761,6 +1802,7 @@ export const stringFromBase64UrlString: Transformation = new Tra * @see {@link stringFromBase64String} * @see `Schema.StringFromHex` - a ready-made schema wrapping this transformation. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1798,6 +1840,7 @@ export const stringFromHexString: Transformation = new Transform * @see {@link stringFromBase64String} * @see `Schema.StringFromUriComponent` - a ready-made schema wrapping this transformation. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -1837,6 +1880,7 @@ export const stringFromUriComponent: Transformation = new Transf * @see {@link uint8ArrayFromBase64String} * @see {@link fromFormData} * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1882,6 +1926,7 @@ export function fromJsonString(options?: { * @see {@link fromURLSearchParams} * @see {@link fromJsonString} * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1919,6 +1964,7 @@ export const fromFormData = new Transformation( * @see {@link fromFormData} * @see {@link fromJsonString} * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -1944,6 +1990,7 @@ export const fromURLSearchParams = new Transformation( * @see {@link timeZoneFromString} for IANA or offset string encodings * @see {@link timeZoneNamedFromString} for IANA named-zone strings * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1971,6 +2018,7 @@ export const timeZoneOffsetFromNumber: Transformation = tra * @see {@link dateFromString} for decoding into JavaScript `Date` * @see {@link dateTimeZonedFromString} for ISO strings that should preserve zoned date-time information * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -2093,6 +2143,7 @@ export const dateTimeUtcFromString: Transformation = trans * * @see {@link dateTimeUtcFromString} for date-time strings that should decode to `DateTime.Utc` and encode as UTC ISO strings * + * @stability stable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Scope.ts b/packages/effect/src/Scope.ts similarity index 98% rename from repos/effect/packages/effect/src/Scope.ts rename to packages/effect/src/Scope.ts index 8e2cf1904..2746d1a2c 100644 --- a/repos/effect/packages/effect/src/Scope.ts +++ b/packages/effect/src/Scope.ts @@ -7,6 +7,7 @@ * and `Layer`, while this module is useful when code needs to create, provide, * fork, close, or inspect scopes directly. * + * @stability stable * @since 2.0.0 */ @@ -39,6 +40,7 @@ const CloseableTypeId = effect.ScopeCloseableTypeId * Effect.runSync(program) // => [["sequential", "Empty"], "Closed"] * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -68,6 +70,7 @@ export interface Scope { * cleanups // => ["Cleanup!"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -94,6 +97,7 @@ export interface Closeable extends Scope { * Effect.runSync(program) // => ["Empty", "Closed"] * ``` * + * @stability stable * @since 4.0.0 */ export declare namespace State { @@ -208,6 +212,7 @@ export declare namespace State { * cleanups // => ["Cleanup"] * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -233,6 +238,7 @@ export const Scope: Context.Service = effect.scopeTag * cleanups // => ["Cleanup 2", "Cleanup 1"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -264,6 +270,7 @@ export const make: (finalizerStrategy?: "sequential" | "parallel") => Effect ["Cleanup"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -303,6 +310,7 @@ export const makeUnsafe: (finalizerStrategy?: "sequential" | "parallel") => Clos * events // => ["working", "cleanup"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -341,6 +349,7 @@ export const provide: { * exits // => [Exit.void] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -375,6 +384,7 @@ export const addFinalizerExit: (scope: Scope, finalizer: (exit: Exit) * events // => ["work", "cleanup 3", "cleanup 2", "cleanup 1"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -408,6 +418,7 @@ export const addFinalizer: (scope: Scope, finalizer: Effect) => Effect< * cleanups // => ["child", "parent"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -449,6 +460,7 @@ export const fork: ( * cleanups // => ["child", "parent"] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -488,6 +500,7 @@ export const forkUnsafe: (scope: Scope, finalizerStrategy?: "sequential" | "para * events // => ["work", "memory", "file", "database"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -515,6 +528,7 @@ export const close: (self: Closeable, exit: Exit) => Effect = * * @see {@link close} for the usual effectful close operation that always returns an `Effect` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -539,6 +553,7 @@ export const closeUnsafe: (self: Closeable, exit_: Exit) => Effect = { * * @see {@link State} for the open/closed cache state that stores entries by key * + * @stability stable * @category models * @since 4.0.0 */ @@ -129,6 +134,7 @@ export interface Entry { * * @see {@link make} for creating a scoped cache with one fixed time-to-live * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -191,6 +197,7 @@ export const makeWith = < * * @see {@link makeWith} for computing time-to-live from each lookup result and key * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -251,6 +258,7 @@ const defaultTimeToLive = (_: Exit.Exit, _key: unknown): Duration.Du * @see {@link getSuccess} for inspecting an already-completed successful entry * @see {@link refresh} for forcing a new lookup * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -283,7 +291,7 @@ export const get: { } MutableHashMap.set(state.map, key, entry) return checkCapacity(fiber, state.map, self.capacity).pipe( - Option.isSome(oentry) ? effect.flatMap(() => Scope.close(oentry.value.scope, effect.exitVoid)) : identity, + Option.isSome(oentry) ? effect.flatMap(() => closeEvicted(oentry.value)) : identity, effect.flatMap(() => { entry.fiber = effect.forkUnsafe( fiber, @@ -348,6 +356,16 @@ const hasExpired = (entry: Entry, fiber: Fiber.Fiber= entry.expiresAt } +// Expiry and capacity eviction report cleanup failures without failing unrelated readers. +const closeEvicted = (entry: Entry): Effect.Effect => + effect.catchCause(Scope.close(entry.scope, effect.exitVoid), reportUnhandledError) + +const reportUnhandledError = (cause: Cause.Cause): Effect.Effect => + core.withFiber((fiber) => { + const level = fiber.getRef(UnhandledLogLevel) + return level ? effect.logWithLevel(level)("Unhandled error in ScopedCache finalizer", cause) : effect.void + }) + const checkCapacity = ( parent: Fiber.Fiber, map: MutableHashMap.MutableHashMap>, @@ -360,7 +378,7 @@ const checkCapacity = ( const fibers = Arr.empty>() for (const [key, entry] of map) { MutableHashMap.remove(map, key) - fibers.push(effect.forkUnsafe(parent as any, Scope.close(entry.scope, effect.exitVoid), true)) + fibers.push(effect.forkUnsafe(parent as any, closeEvicted(entry), true)) diff-- if (diff === 0) break } @@ -384,6 +402,7 @@ const checkCapacity = ( * @see {@link get} for running the lookup on missing or expired keys * @see {@link getSuccess} for inspecting only already-completed successful entries * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -419,7 +438,7 @@ const getImpl = ( } else if (hasExpired(oentry.value, fiber)) { MutableHashMap.remove(state.map, key) return effect.as( - Scope.close(oentry.value.scope, effect.exitVoid), + closeEvicted(oentry.value), undefined ) } else if (isRead) { @@ -446,6 +465,7 @@ const getImpl = ( * @see {@link get} for awaiting or starting the lookup effect * @see {@link getOption} for awaiting an already-cached entry without starting a lookup * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -488,6 +508,7 @@ export const getSuccess: { * @see {@link get} for reading or computing a cached value * @see {@link refresh} for replacing an entry by running the lookup function * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -541,6 +562,7 @@ export const set: { * @see {@link getOption} for reading an existing cached entry * @see {@link get} for running the lookup on missing or expired keys * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -575,6 +597,7 @@ export const has: { * @see {@link invalidateWhen} for invalidating only when a cached value matches a predicate * @see {@link invalidateAll} for removing every cached entry * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -616,6 +639,7 @@ export const invalidate: { * * @see {@link invalidate} for unconditional removal by key * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -668,6 +692,7 @@ export const invalidateWhen: { * @see {@link get} for reusing an unexpired entry before running the lookup * @see {@link invalidate} for removing an entry without recomputing it * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -726,6 +751,7 @@ export const refresh: { * * @see {@link invalidate} for removing one cached entry * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -764,6 +790,7 @@ const invalidateAllImpl = ( * The size reflects the current number of entries stored, not the number * of valid entries. * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -784,6 +811,7 @@ export const size = (self: ScopedCache): Effect.Effe * @see {@link entries} for retrieving successful cached key-value pairs * @see {@link values} for retrieving only successfully cached values * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -821,6 +849,7 @@ export const keys = (self: ScopedCache): Effect.Effe * @see {@link entries} for retrieving successful cached key-value pairs * @see {@link keys} for retrieving only cached keys * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -844,6 +873,7 @@ export const values = (self: ScopedCache): Effect.Ef * @see {@link keys} for retrieving only cached keys * @see {@link values} for retrieving only cached values * + * @stability stable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ScopedRef.ts b/packages/effect/src/ScopedRef.ts similarity index 97% rename from repos/effect/packages/effect/src/ScopedRef.ts rename to packages/effect/src/ScopedRef.ts index af4a352f7..e8aa04951 100644 --- a/repos/effect/packages/effect/src/ScopedRef.ts +++ b/packages/effect/src/ScopedRef.ts @@ -7,6 +7,7 @@ * value. Reads can be effectful or synchronous, and updates are synchronized so * only one replacement happens at a time. * + * @stability stable * @since 2.0.0 */ import * as Effect from "./Effect.ts" @@ -32,6 +33,7 @@ const TypeId = "~effect/ScopedRef" * later replace it with another acquired value while ensuring the previous * value is released. * + * @stability stable * @category models * @since 2.0.0 */ @@ -78,6 +80,7 @@ const isClosed = (scope: Scope.Scope): boolean => scope.state._tag === "Closed" * resources that must be released. * * @see {@link make} for creating a `ScopedRef` from a value that does not require resource acquisition + * @stability stable * @category constructors * @since 2.0.0 */ @@ -106,6 +109,7 @@ export const fromAcquire: ( * * @see {@link get} for Effect-wrapped access in Effect programs * + * @stability stable * @category getters * @since 4.0.0 */ @@ -121,6 +125,7 @@ export const getUnsafe = (self: ScopedRef): A => self.backing.backing.ref. * * @see {@link getUnsafe} for reading the current value synchronously when an unsafe read is acceptable * + * @stability stable * @category getters * @since 2.0.0 */ @@ -148,6 +153,7 @@ export const get = (self: ScopedRef): Effect.Effect => Effect.sync(() = * @see {@link fromAcquire} for creating a `ScopedRef` from an effect that acquires the initial value * @see {@link set} for replacing the current value with a newly acquired value * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -169,6 +175,7 @@ export const make = (evaluate: LazyArg): Effect.Effect, never * changed to the new value, with old resources released, or until the attempt * to acquire a new value fails. * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Semaphore.ts b/packages/effect/src/Semaphore.ts similarity index 98% rename from repos/effect/packages/effect/src/Semaphore.ts rename to packages/effect/src/Semaphore.ts index eaa8e59c6..195513a22 100644 --- a/repos/effect/packages/effect/src/Semaphore.ts +++ b/packages/effect/src/Semaphore.ts @@ -8,6 +8,7 @@ * work that should only run immediately, and resizing support for an existing * semaphore. * + * @stability stable * @since 4.0.0 */ import type * as Effect from "./Effect.ts" @@ -51,6 +52,7 @@ import type * as Option from "./Option.ts" * @see {@link make} for creating a semaphore inside Effect code * @see {@link makeUnsafe} for creating a semaphore synchronously * + * @stability stable * @category models * @since 4.0.0 */ @@ -206,6 +208,7 @@ export interface Semaphore { * await Effect.runPromise(program) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -359,6 +362,7 @@ class SemaphoreImpl implements Semaphore { * await Effect.runPromise(program) // => [1, 2, 3, 4] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -381,6 +385,7 @@ export const make = (permits: number): Effect.Effect => internal.sync * @see {@link make} for creating a semaphore with an initial permit count * @see {@link release} for returning permits without changing semaphore capacity * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -408,6 +413,7 @@ export const resize: { * @see {@link take} for manually acquiring permits * @see {@link release} for manually returning permits * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -433,6 +439,7 @@ export const withPermits: { * @see {@link take} for manually acquiring permits * @see {@link release} for manually returning permits * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -462,6 +469,7 @@ export const withPermit: { * * @see {@link withPermits} for the variant that waits until permits are available * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -495,6 +503,7 @@ export const withPermitsIfAvailable: { * @see {@link takeIfAvailable} for manually acquiring permits without waiting * @see {@link release} for returning manually acquired permits * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -522,6 +531,7 @@ export const take: { * @see {@link release} for returning manually acquired permits * @see {@link withPermitsIfAvailable} for automatic acquisition and release around an effect * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -555,6 +565,7 @@ export const takeIfAvailable: { * @see {@link releaseAll} for returning every currently taken permit * @see {@link withPermits} for automatic acquire and release around an effect * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -582,6 +593,7 @@ export const release: { * @see {@link release} for releasing a known permit count * @see {@link withPermits} for automatic acquire and release around an effect * + * @stability stable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Sink.ts b/packages/effect/src/Sink.ts similarity index 97% rename from repos/effect/packages/effect/src/Sink.ts rename to packages/effect/src/Sink.ts index ddf3fdfe7..0074c1290 100644 --- a/repos/effect/packages/effect/src/Sink.ts +++ b/packages/effect/src/Sink.ts @@ -7,6 +7,7 @@ * search, count, or otherwise reduce streamed input, and they can be composed * when a stream needs more than one consuming step. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyReadonlyArray } from "./Array.ts" @@ -58,6 +59,7 @@ const TypeId = "~effect/Sink" * await Effect.runPromise(Stream.run(stream, sink)) // => 42 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -81,6 +83,7 @@ export interface Sink) * include sinks. Users normally do not need to reference this interface * directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -123,6 +127,7 @@ export interface SinkUnify extends Effec * sink-specific unification should preserve the `Sink` type parameters. Users * normally do not need to reference this interface directly. * + * @stability stable * @category models * @since 2.0.0 */ @@ -133,6 +138,7 @@ export interface SinkUnifyIgnore { /** * Namespace containing types and interfaces for Sink variance and type relationships. * + * @stability stable * @since 2.0.0 */ export declare namespace Sink { @@ -202,6 +208,7 @@ const SinkProto = { * Sink.isSink(notStream) // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -230,6 +237,7 @@ export const isSink = (u: unknown): u is Sink( * written // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -297,6 +306,7 @@ export const fromWritableStream = (options: { * active scope, and returns an effect that completes with the sink's `End` * value. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -326,6 +336,7 @@ export const fromTransform = ( * await Effect.runPromise(Channel.runDrain(channel)) // => [6] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -348,6 +359,7 @@ export const toChannel = ( * provided pipeline, and uses the final effect's success value as the sink * result. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -356,7 +368,7 @@ export const make = (): make.Constructor => (...fns: []) => pipe( internalStream.fromChannel(Channel.fromPull(Effect.succeed(upstream))), ...fns as any as [() => Effect.Effect], - Effect.flatMap((a) => Cause.done>([a])), + Effect.map((a) => [a] as End), Scope.provide(scope) ) ) @@ -365,6 +377,7 @@ export const make = (): make.Constructor => (...fns: []) => * Companion namespace containing overload types for the pipe-style sink * constructor returned by `Sink.make`. * + * @stability stable * @since 4.0.0 */ export declare namespace make { @@ -479,6 +492,7 @@ export declare namespace make { * Use when you need to create a sink from an effect that returns both the sink * result value and optional leftovers. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -494,6 +508,7 @@ export const fromEffectEnd = ( * * If the effect fails, the sink fails with the same error. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -509,6 +524,7 @@ export const fromEffect = ( * When the upstream stream ends, the sink ends the queue and completes with * `void`. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -533,6 +549,7 @@ export const fromQueue = ( * * The sink completes with `void` when the upstream stream ends. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -556,6 +573,7 @@ export const fromPubSub = ( * await Effect.runPromise(Stream.run(stream, sink)) // => 42 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -565,6 +583,7 @@ export const succeed = (a: A, leftovers?: NonEmptyReadonlyArray /** * A sink that immediately ends with the specified lazily evaluated value. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -573,6 +592,7 @@ export const sync = (a: LazyArg): Sink => fromEffect(Effect.sync(a)) /** * A sink that is created from a lazily evaluated sink. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -595,6 +615,7 @@ export const suspend = (evaluate: LazyArg>) * await Effect.runPromiseExit(Stream.run(stream, sink)) // => Exit.fail("Sink failed") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -616,6 +637,7 @@ export const fail = (e: E): Sink => fromEffectEnd(E * await Effect.runPromiseExit(Stream.run(stream, sink)) // => Exit.fail("Lazy error") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -638,6 +660,7 @@ export const failSync = (evaluate: LazyArg): Sink Exit.fail("Custom cause") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -660,6 +683,7 @@ export const failCause = (cause: Cause.Cause): Sink Exit.fail("Lazy cause") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -682,6 +706,7 @@ export const failCauseSync = (evaluate: LazyArg>): Sink Exit.die("Defect error") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -690,6 +715,7 @@ export const die = (defect: unknown): Sink => fromEffectEnd(Effect.die(de /** * A sink that never completes. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -704,6 +730,7 @@ export const never: Sink = fromEffectEnd(Effect.never) * instead of being returned to downstream sink composition. This does not * continue pulling additional elements from the upstream stream. * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -721,6 +748,7 @@ export const ignoreLeftover = (self: Sink): Sink * @see {@link count} for consuming all input while returning the number of elements * @see {@link forEach} for consuming all input while running an effect for each element * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -750,6 +778,7 @@ export const drain: Sink = fromTransform((upstream) => * @see {@link foldArray} for folding each pulled non-empty input array at once * @see {@link foldUntil} for folding until a fixed maximum number of elements is consumed * + * @stability stable * @category folding * @since 2.0.0 */ @@ -794,6 +823,7 @@ export const fold = ( * @see {@link fold} for folding element by element and returning leftovers when stopping mid-array * @see {@link reduceWhileArrayEffect} for array-level effectful reducing that checks the predicate before consuming input * + * @stability stable * @category folding * @since 4.0.0 */ @@ -829,6 +859,7 @@ export const foldArray = ( * If the sink stops in the middle of a pulled array, the remaining elements * from that array are returned as leftovers. * + * @stability stable * @category folding * @since 2.0.0 */ @@ -858,6 +889,7 @@ export const foldUntil = ( * * @see {@link some} for the dual any-match check * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -878,6 +910,7 @@ export const every = (predicate: Predicate): Sink => * * @see {@link every} for the all-match check * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -905,6 +938,7 @@ export const some = (predicate: Predicate): Sink => * @see {@link as} for replacing the result with a constant value * @see {@link mapEnd} for transforming both the result and leftovers * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -927,6 +961,7 @@ export const map: { * * @see {@link map} for computing the replacement from the original result * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -941,6 +976,7 @@ export const as: { /** * Transforms this sink's input elements. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -956,6 +992,7 @@ export const mapInput: { /** * Transforms this sink's input elements effectfully. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -979,6 +1016,7 @@ export const mapInputEffect: { * Transforms each non-empty array of upstream input before it is fed to this * sink. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1002,6 +1040,7 @@ export const mapInputArray: { * Transforms each non-empty array of upstream input effectfully before it is * fed to this sink. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1034,6 +1073,7 @@ export const mapInputArrayEffect: { * * This can change both the result value and the optional leftovers. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1069,6 +1109,7 @@ const transformEffect = ( * This can change both the result value and the optional leftovers, and the * transformation can fail or require services. * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1102,6 +1143,7 @@ export const mapEffectEnd: { * @see {@link mapEffectEnd} for effectfully transforming both the result and leftovers * @see {@link flatMap} for continuing with another sink based on the result * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1121,6 +1163,7 @@ export const mapEffect: { /** * Transforms the errors emitted by this sink using `f`. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1135,6 +1178,7 @@ export const mapError: { /** * Transforms the leftovers emitted by this sink using `f`. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1156,6 +1200,7 @@ export const mapLeftover: { * pulled than needed, the remaining elements from the same array are returned * as leftovers. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1211,6 +1256,7 @@ export const take = (n: number): Sink, In, In> => { * @see {@link map} for transforming the result without switching sinks * @see {@link mapEffect} for effectfully transforming the result without switching sinks * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1260,6 +1306,7 @@ export const flatMap: { * A sink that reduces input elements from the provided `initial` state with * `f` while the specified `predicate` returns `true`. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1296,6 +1343,7 @@ export const reduceWhile = ( * A sink that effectfully reduces input elements from the provided `initial` * state with `f` while the specified `predicate` returns `true`. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1337,6 +1385,7 @@ export const reduceWhileEffect = ( * A sink that reduces non-empty input arrays from the provided `initial` state * with `f` while the specified `predicate` returns `true`. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1367,6 +1416,7 @@ export const reduceWhileArray = ( * A sink that effectfully reduces non-empty input arrays from the provided * `initial` state with `f` while the specified `predicate` returns `true`. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1398,6 +1448,7 @@ export const reduceWhileArrayEffect = ( * A sink that reduces its inputs using the provided function `f` starting from * the provided `initial` state. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1413,6 +1464,7 @@ export const reduce = (initial: LazyArg, f: (s: S, input: In) => S): S * A sink that reduces its inputs using the provided function `f` starting from * the specified `initial` state. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1436,6 +1488,7 @@ export const reduceArray = ( * A sink that reduces its inputs using the provided effectful function `f` * starting from the specified `initial` state. * + * @stability stable * @category folding * @since 4.0.0 */ @@ -1455,6 +1508,7 @@ const head_ = reduceWhile(Option.none, Option.isNone, (_, in_) => Optio * upstream ends without input. The first element is consumed; later elements * from the same pulled array are emitted as leftovers. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1482,6 +1536,7 @@ const last_ = reduceArray(Option.none, (_, arr) => Arr.last(arr)) * * @see {@link head} for taking the first input value instead * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1504,6 +1559,7 @@ export const last = (): Sink, In> => last_ as any * * @see {@link findEffect} for an effectful predicate that can fail or require services * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1533,6 +1589,7 @@ export const find: { * * @see {@link find} for the synchronous predicate variant * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1548,6 +1605,7 @@ export const findEffect = ( /** * Creates a sink which sums up its inputs. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1565,6 +1623,7 @@ export const sum: Sink = reduceArray(() => 0, (s, arr) => { * * Use to consume input and return only the number of elements received. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1579,6 +1638,7 @@ export const count: Sink = reduceArray(() => 0, (s, arr) => s + * * @see {@link take} for collecting only a fixed number of input elements * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1597,6 +1657,7 @@ export const collect = (): Sink, In> => * The first failing input is consumed and excluded from the result. Any later * elements from the same pulled array are returned as leftovers. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1634,6 +1695,7 @@ export const takeWhile: { * result. Any later elements from the same pulled array are returned as * leftovers. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1670,6 +1732,7 @@ export const takeWhileFilter = ( * excluded from the result. Any later elements from the same pulled array are * returned as leftovers. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1720,6 +1783,7 @@ export const takeWhileEffect: { * result. Any later elements from the same pulled array are returned as * leftovers. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1756,6 +1820,7 @@ export const takeWhileFilterEffect = ( * Collects input elements until the predicate returns `true`, including the * matching element in the result. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1777,6 +1842,7 @@ export const takeUntil = (predicate: Predicate): Sink, In, In> * * If the predicate effect fails, the sink fails with the same error. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1814,6 +1880,7 @@ export const takeUntilEffect = ( * processed // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1839,6 +1906,7 @@ export const forEach = ( * processed // => [[1, 2, 3, 4, 5]] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1861,6 +1929,7 @@ export const forEachArray = ( * The sink stops consuming input when the function returns `false` or when the * upstream stream ends, and completes with `void`. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1884,6 +1953,7 @@ export const forEachWhile = ( * The sink stops consuming input when the function returns `false` or when the * upstream stream ends, and completes with `void`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1920,6 +1990,7 @@ export const forEachWhileArray = ( * processed // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1930,6 +2001,7 @@ export const unwrap = ( /** * Runs a summary effect when the sink starts and again when it completes. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1958,6 +2030,7 @@ export const summarized: { /** * Returns the sink that executes this one and times its execution. * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1969,6 +2042,7 @@ export const withDuration = ( /** * A sink that drains all input and returns the elapsed duration. * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1982,6 +2056,7 @@ export const timed: Sink = map(withDuration(drain), * Services contained in the provided context are removed from the sink's * service requirements. * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -2011,6 +2086,7 @@ export const provideContext: { * The service identified by `key` is removed from the sink's service * requirements. * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -2044,6 +2120,7 @@ export const provideService: { * upstream stream. If the upstream stream had already ended, the fallback sees * the upstream end instead. * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -2097,13 +2174,14 @@ export const orElse: { * @see {@link catch_ catch} for recovering from typed errors only * @see {@link orElse} for recovering by switching to another sink * + * @stability stable * @category error handling * @since 4.0.0 */ export const catchCause: { ( f: (error: Cause.Cause>) => Effect.Effect - ): (self: Sink) => Sink + ): (self: Sink) => Sink ( self: Sink, f: (error: Cause.Cause) => Effect.Effect @@ -2146,6 +2224,7 @@ export { * @see {@link catchCause} for recovering from the full failure cause * @see {@link orElse} for recovering by switching to another sink * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2160,6 +2239,7 @@ export { * The effect receives the sink's `Exit` for the result value. The original * sink result and leftovers are preserved unless the finalizer itself fails. * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -2188,6 +2268,7 @@ export const onExit: { * The original sink result and leftovers are preserved unless the finalizer * itself fails. * + * @stability stable * @category resource management * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/StandardSchema.ts b/packages/effect/src/StandardSchema.ts similarity index 99% rename from repos/effect/packages/effect/src/StandardSchema.ts rename to packages/effect/src/StandardSchema.ts index 885765170..7108dc2a7 100644 --- a/repos/effect/packages/effect/src/StandardSchema.ts +++ b/packages/effect/src/StandardSchema.ts @@ -3,6 +3,7 @@ * * The declarations below are vendored verbatim from `@standard-schema/spec` 1.1.0. * + * @stability stable * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Stdio.ts b/packages/effect/src/Stdio.ts similarity index 96% rename from repos/effect/packages/effect/src/Stdio.ts rename to packages/effect/src/Stdio.ts index d51caa541..858e533ea 100644 --- a/repos/effect/packages/effect/src/Stdio.ts +++ b/packages/effect/src/Stdio.ts @@ -9,6 +9,7 @@ * provides a constructor for service values and a small test layer with * overridable defaults. * + * @stability unstable * @since 4.0.0 */ import * as Context from "./Context.ts" @@ -25,6 +26,7 @@ import * as Stream from "./Stream.ts" * * Use to type the runtime identifier stored on `Stdio` service implementations. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -38,6 +40,7 @@ export type TypeId = "~effect/Stdio" * This marker is part of the runtime representation of `Stdio` service * implementations. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -57,6 +60,7 @@ export const TypeId: TypeId = "~effect/Stdio" * standard error, and a stream of standard input bytes. I/O operations can fail * with `PlatformError`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -94,6 +98,7 @@ export interface Stdio { * @see {@link make} for constructing a `Stdio` service directly * @see {@link layerTest} for a test layer with defaults and overrides * + * @stability unstable * @category services * @since 4.0.0 */ @@ -116,6 +121,7 @@ export const Stdio: Context.Service = Context.Service(TypeI * * @see {@link layerTest} for a test layer with default fields that can be overridden * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -146,6 +152,7 @@ export const make = ( * * @see {@link make} for constructing a `Stdio` service directly without a `Layer` or defaults * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Stream.ts b/packages/effect/src/Stream.ts similarity index 98% rename from repos/effect/packages/effect/src/Stream.ts rename to packages/effect/src/Stream.ts index 96eda628b..a6d1bcdce 100644 --- a/repos/effect/packages/effect/src/Stream.ts +++ b/packages/effect/src/Stream.ts @@ -8,6 +8,7 @@ * stream lifecycle: create a stream, transform or combine it, control buffering * and timing, handle failures, and finally consume it. * + * @stability stable * @since 2.0.0 */ // @effect-diagnostics returnEffectInGen:off @@ -75,6 +76,7 @@ import type * as Unify from "./Unify.ts" /** * String literal type used as the unique brand for `Stream` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -91,6 +93,7 @@ export type TypeId = "~effect/Stream" * * @see {@link isStream} for the public guard that checks this identifier * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -120,6 +123,7 @@ export const TypeId: TypeId = "~effect/Stream" * values // => [2, 4, 6] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -133,6 +137,7 @@ export interface Stream extends Variance extends Eff /** * Type-level marker that excludes Stream from unification. * + * @stability stable * @category models * @since 2.0.0 */ @@ -165,6 +171,7 @@ export interface StreamUnifyIgnore { * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3] * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -180,6 +187,7 @@ export interface StreamTypeLambda extends TypeLambda { * The emitted value `A`, error `E`, and service requirement `R` type * parameters are covariant. * + * @stability stable * @category models * @since 2.0.0 */ @@ -195,6 +203,7 @@ export interface Variance { * * `_A`, `_E`, and `_R` are covariant markers. * + * @stability stable * @category models * @since 3.4.0 */ @@ -217,6 +226,7 @@ export interface VarianceStruct { * const value: SuccessType = 42 * ``` * + * @stability stable * @category utility types * @since 3.4.0 */ @@ -235,6 +245,7 @@ export type Success = T extends Stream ? _A : n * const error: ErrorType = "boom" * ``` * + * @stability stable * @category utility types * @since 3.4.0 */ @@ -257,6 +268,7 @@ export type Error = T extends Stream ? _E : nev * services.db.query("SELECT 1") // => "SELECT 1" * ``` * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -274,6 +286,7 @@ export type Services = T extends Stream ? _R : * Stream.isStream({ data: [1, 2, 3] }) // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -290,6 +303,7 @@ export const isStream = (u: unknown): u is Stream => * Stream.DefaultChunkSize // => 4096 * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -298,6 +312,7 @@ export const DefaultChunkSize: number = Channel.DefaultChunkSize /** * Describes how merged streams decide when to halt. * + * @stability stable * @category models * @since 4.0.0 */ @@ -316,6 +331,7 @@ export type HaltStrategy = Channel.HaltStrategy * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -335,6 +351,7 @@ export const fromChannel: , E, R>( * await Effect.runPromise(Stream.runCollect(stream)) // => [42] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -367,6 +384,7 @@ export const fromEffect = (effect: Effect.Effect): Stream ["Hello, World!"] * ``` * + * @stability stable * @category accessors * @since 4.0.0 */ @@ -409,6 +427,7 @@ export const service = (service: Context.Key): Stream = * ) // => ["Hello, World!"] * ``` * + * @stability stable * @category accessors * @since 4.0.0 */ @@ -432,6 +451,7 @@ export const serviceOption = (service: Context.Key): Stream true * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -451,6 +471,7 @@ export const fromEffectDrain = (effect: Effect.Effect): Stream * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3, 4, 5] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -470,6 +491,7 @@ export const fromEffectRepeat = (effect: Effect.Effect): Strea * await Effect.runPromise(Stream.runCollect(stream)) // => ["ping", "ping", "ping"] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -509,6 +531,7 @@ export const fromEffectSchedule = ( * await Effect.runPromise(Stream.tick(0).pipe(Stream.take(3), Stream.runCollect)) // => [undefined, undefined, undefined] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -551,6 +574,7 @@ export const tick = (interval: Duration.Input): Stream => * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -573,6 +597,7 @@ export const fromPull = ( * await Effect.runPromise(Stream.runCollect(transformed)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -619,6 +644,7 @@ export const transformPull = ( * finalized // => [true] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -653,6 +679,7 @@ export const transformPullBracket = ( * values.flat() // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -691,6 +718,7 @@ export const toChannel = ( * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -713,6 +741,7 @@ export const callback = ( * await Effect.runPromise(Stream.runCollect(Stream.empty)) // => [] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -729,6 +758,7 @@ export const empty: Stream = fromChannel(Channel.empty) * await Effect.runPromise(Stream.runCollect(Stream.succeed(3))) // => [3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -747,6 +777,7 @@ export const succeed = (value: A): Stream => fromChannel(Channel.succeed(A * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -767,6 +798,7 @@ export const make = >(...values: As): Stream * await Effect.runPromise(Stream.sync(() => 2 + 1).pipe(Stream.runCollect)) // => [3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -787,6 +819,7 @@ export const sync = (evaluate: LazyArg): Stream => fromChannel(Channel. * await Effect.runPromise(Stream.suspend(() => Stream.make(1, 2, 3)).pipe(Stream.runCollect)) // => [1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -804,6 +837,7 @@ export const suspend = (stream: LazyArg>): Stream Exit.fail("Uh oh!") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -822,6 +856,7 @@ export const fail = (error: E): Stream => fromChannel(Channel.fail( * await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.exit)) // => Exit.fail("Uh oh!") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -842,6 +877,7 @@ export const failSync = (evaluate: LazyArg): Stream => fromChann * await Effect.runPromise(Stream.runCollect(stream)) // => ["recovered"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -861,6 +897,7 @@ export const failCause = (cause: Cause.Cause): Stream => fromCha * await Effect.runPromise(Effect.exit(Stream.runCollect(stream))) // => Exit.failCause(Cause.die(defect)) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -881,6 +918,7 @@ export const die = (defect: unknown): Stream => fromChannel(Channel.die(d * await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.exit)) // => Exit.fail("Connection timeout after retries") * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -917,6 +955,7 @@ export const failCauseSync = (evaluate: LazyArg>): Stream(iterator: IterableIterator, maxChunkSi * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -997,6 +1037,7 @@ export const fromIterable = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1022,6 +1063,7 @@ export const fromIterableEffect = (iterable: Effect.Effect, * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1046,6 +1088,7 @@ export const fromIterableEffectRepeat = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1074,6 +1117,7 @@ export const fromArray = (array: ReadonlyArray): Stream => * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1098,6 +1142,7 @@ export const fromArrayEffect = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1133,6 +1178,7 @@ export const fromArrays = >>( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1167,6 +1213,7 @@ export const fromQueue = (queue: Queue.Dequeue): Stream(pubsub: PubSub.PubSub): Stream => fromChanne * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -1243,6 +1291,7 @@ export const fromPubSubTake = (pubsub: PubSub.PubSub>): St * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1278,6 +1327,7 @@ export const fromReadableStream = ( * * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1305,6 +1355,7 @@ export const fromAsyncIterable = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1345,6 +1396,7 @@ export const fromSchedule = (schedule: Schedule.Schedule(pubsub: PubSub.Subscription): Stream = /** * Interface representing an event listener target. * + * @stability stable * @category models * @since 3.4.0 */ @@ -1406,6 +1459,7 @@ export interface EventListener { * * ``` * + * @stability stable * @category constructors * @since 3.1.0 */ @@ -1455,6 +1509,7 @@ export const fromEventListener = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1500,6 +1555,7 @@ export const unfold = ( * await Effect.runPromise(Stream.runCollect(stream)) // => [0, 1, 2, 3] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1544,6 +1600,7 @@ export const paginate = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1572,6 +1629,7 @@ export const iterate = (value: A, next: (value: A) => A): Stream => * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1614,6 +1672,7 @@ export const range = ( * await Effect.runPromise(program) // => [] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1638,6 +1697,7 @@ export const never: Stream = fromChannel(Channel.never) * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -1671,6 +1731,7 @@ export const unwrap = ( * events // => ["acquire", "release"] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -1690,6 +1751,7 @@ export const scoped = ( * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5] * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1726,6 +1788,7 @@ export const map: { * * @see {@link map} for deriving the replacement value from each element * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1765,6 +1828,7 @@ export const as: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1805,6 +1869,7 @@ export const mapBoth: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -1855,6 +1920,7 @@ export const mapArray: { * events // => ["Processing: 1", "Processing: 2", "Processing: 3"] * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -1912,6 +1978,7 @@ export const mapEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -1969,6 +2036,7 @@ export const flattenEffect: < * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -2013,6 +2081,7 @@ export const mapArrayEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -2046,6 +2115,7 @@ export const result = (self: Stream): Stream ["before mapping: 1", "after mapping: 2", "before mapping: 2", "after mapping: 4", "before mapping: 3", "after mapping: 6"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2102,6 +2172,7 @@ export const tap: { * events // => ["seen: 1", "seen: 2", "error: boom"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2159,6 +2230,7 @@ export const tapBoth: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2205,16 +2277,22 @@ export const tapSink: { const pullAndOffer = pull.pipe( Effect.flatMap((chunk_) => { + // The sink may have exited while the upstream pull was suspended. + if (causeSink) return Effect.failCause(causeSink) + if (sinkDone) return Effect.succeed(chunk_) chunk = chunk_ sinkLatch.closeUnsafe() upstreamLatch.openUnsafe() return Effect.as(sinkLatch.await, chunk_) }), - Pull.catchDone(() => { + Pull.catchDone((): Effect.Effect => { + if (causeSink) return Effect.failCause(causeSink) + if (sinkDone) return Cause.done() streamDone = true sinkLatch.closeUnsafe() upstreamLatch.openUnsafe() - return Effect.flatMap(sinkLatch.await, () => Cause.done()) + return Effect.flatMap(sinkLatch.await, (): Pull.Pull => + causeSink ? Effect.failCause(causeSink) : Cause.done()) }) ) @@ -2256,6 +2334,7 @@ export const tapSink: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2309,6 +2388,7 @@ export const flatMap: { * })) * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2371,6 +2451,7 @@ export const switchMap: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2418,6 +2499,7 @@ export const flatten: < * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2440,6 +2522,7 @@ export const flattenArray = (self: Stream, * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2470,6 +2553,7 @@ export const drain = (self: Stream): Stream => fr * events // => ["background task"] * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2502,6 +2586,7 @@ export const drainFork: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -2550,6 +2635,7 @@ export const repeat: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category rate limiting * @since 2.0.0 */ @@ -2592,6 +2678,7 @@ export const schedule: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category delays & timeouts * @since 2.0.0 */ @@ -2628,6 +2715,7 @@ export const timeout: { * * @see {@link timeout} for ending the stream instead of switching to a fallback stream * + * @stability stable * @category delays & timeouts * @since 4.0.0 */ @@ -2720,6 +2808,7 @@ export const timeoutOrElse: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -2789,6 +2878,7 @@ export const repeatElements: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category repetition * @since 2.0.0 */ @@ -2811,6 +2901,7 @@ export const forever = (self: Stream): Stream => from * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -2840,6 +2931,7 @@ export const flattenIterable = (self: Stream, E, R>): Strea * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2867,6 +2959,7 @@ export const flattenTake = (self: Stream, E2, R>): * })) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2919,6 +3012,7 @@ function* concatChannels(self: Stream, that: Stream) * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -2954,6 +3048,7 @@ export const prepend: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category merging * @since 2.0.0 */ @@ -3014,6 +3109,7 @@ export const merge: { * events // => ["side task"] * ``` * + * @stability stable * @category merging * @since 4.0.0 */ @@ -3065,6 +3161,7 @@ export const mergeEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category merging * @since 4.0.0 */ @@ -3114,6 +3211,7 @@ export const mergeResult: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category merging * @since 2.0.0 */ @@ -3160,6 +3258,7 @@ export const mergeLeft: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category merging * @since 2.0.0 */ @@ -3209,6 +3308,7 @@ export const mergeRight: { * @see {@link merge} for merging exactly two streams and choosing a halt strategy * @see {@link flatten} for flattening a stream that already emits streams * + * @stability stable * @category merging * @since 2.0.0 */ @@ -3257,6 +3357,7 @@ export const mergeAll: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3293,6 +3394,7 @@ export const cross: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3333,6 +3435,7 @@ export const crossWith: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3399,6 +3502,7 @@ const zipArrays = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 4.0.0 */ @@ -3502,6 +3606,7 @@ export const zipWithArray: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3540,6 +3645,7 @@ export const zip: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3581,6 +3687,7 @@ export const zipLeft: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3631,6 +3738,7 @@ export const zipRight: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3669,6 +3777,7 @@ export const zipFlatten: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3691,6 +3800,7 @@ export const zipWithIndex = (self: Stream): Stream<[A, number] * })) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3732,6 +3842,7 @@ export const zipWithNext = (self: Stream): Stream<[A, Option.O * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3765,6 +3876,7 @@ export const zipWithPrevious = (self: Stream): Stream<[Option. * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3833,6 +3945,7 @@ export const zipWithPreviousAndNext = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 3.3.0 */ @@ -3848,6 +3961,7 @@ export const zipLatestAll = >>( const latest: Array = [] const emitted = new Set() const readyLatch = Latch.makeUnsafe() + const emptyLatch = Latch.makeUnsafe() return Channel.mergeAll( Channel.fromArray( streams.map((s, i) => @@ -3864,7 +3978,9 @@ export const zipLatestAll = >>( } return Effect.succeed(Arr.of(latest.slice())) }), - Channel.filter(isNotUndefined) + Channel.filter(isNotUndefined), + // An input ending without a value makes a complete tuple impossible. + Channel.mapDoneEffect(() => emitted.has(i) ? Effect.void : emptyLatch.open) ) ) ), @@ -3872,7 +3988,7 @@ export const zipLatestAll = >>( concurrency: "unbounded", bufferSize: 0 } - ) + ).pipe(Channel.interruptWhen(emptyLatch.await)) })) as any /** @@ -3905,6 +4021,7 @@ export const zipLatestAll = >>( * await Effect.runPromise(program) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -3958,6 +4075,7 @@ export const zipLatest: { * })) * ``` * + * @stability stable * @category zipping * @since 2.0.0 */ @@ -4006,6 +4124,7 @@ export const zipLatestWith: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category racing * @since 3.5.0 */ @@ -4066,6 +4185,7 @@ export const raceAll = >>( * await Effect.runPromise(program) * ``` * + * @stability stable * @category racing * @since 3.7.0 */ @@ -4101,6 +4221,7 @@ export const race: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4136,6 +4257,7 @@ export const filter: { * @see {@link filterMapEffect} for an effectful `Filter` * @see {@link partition} for consuming both filter success and failure values * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4173,6 +4295,7 @@ export const filterMap: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4209,6 +4332,7 @@ export const filterEffect: { * @see {@link filterEffect} for effectfully keeping original elements * @see {@link mapEffect} for effectfully transforming every element * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4258,6 +4382,7 @@ export const filterMapEffect: { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -4351,6 +4476,14 @@ export const partitionQueue: { ) ) +const fromQueueShutdownOnEnd = (queue: Queue.Dequeue): Stream> => + fromChannel(Channel.fromTransform((_, scope) => + Effect.as( + Scope.addFinalizer(scope, Queue.shutdown(queue as Queue.Queue)), + Queue.takeAll(queue) + ) + )) + /** * Splits a stream with an effectful `Filter`, returning scoped streams for * filter successes and failures. @@ -4370,6 +4503,7 @@ export const partitionQueue: { * @see {@link partitionQueue} for the lower-level queue result * @see {@link filterMapEffect} for effectful filtering that discards failed filter results * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -4423,7 +4557,7 @@ export const partitionEffect: { (result) => result, options ), - ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const + ([passes, fails]) => [fromQueueShutdownOnEnd(passes), fromQueueShutdownOnEnd(fails)] as const ) ) @@ -4457,6 +4591,7 @@ export const partitionEffect: { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4499,7 +4634,7 @@ export const partition: { > => Effect.map( partitionQueue(self, filter, { capacity: options?.capacity ?? 16 }), - ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const + ([passes, fails]) => [fromQueueShutdownOnEnd(passes), fromQueueShutdownOnEnd(fails)] as const ) ) @@ -4522,6 +4657,7 @@ export const partition: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -4569,6 +4705,7 @@ export const when: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -4596,12 +4733,14 @@ export const peel: { return Effect.failCause(cause_) }) - let stream = fromPull(Effect.succeed(pull)) as Stream - const leftover = yield* run(stream, sink) - if (cause) return [leftover, empty] - - stream = fromPull(Effect.succeed(originalPull)) - return [leftover, stream] + const [result, leftover] = yield* Effect.scopedWith((scope) => + sink.transform(pull as Pull.Pull>, scope) + ) + let stream: Stream = cause ? empty : fromPull(Effect.succeed(originalPull)) + if (leftover) { + stream = concat(fromArray(leftover), stream) + } + return [result, stream] }) ) @@ -4632,6 +4771,7 @@ export const peel: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category buffering * @since 2.0.0 */ @@ -4684,6 +4824,7 @@ export const buffer: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category buffering * @since 4.0.0 */ @@ -4736,6 +4877,7 @@ export const bufferArray: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4776,6 +4918,7 @@ export const catchCause: { * result // => ["recovered: boom"] * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4821,6 +4964,7 @@ export const catchDefect: { * observations // => [false] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4876,6 +5020,7 @@ export { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -4906,6 +5051,7 @@ export { * errors // => ["boom"] * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -4961,6 +5107,7 @@ export const tapError: { * @see {@link tapError} for peeking at every typed error * @see {@link catchTag} for recovering from a tagged error * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5041,6 +5188,7 @@ export const tapErrorTag: { * @see {@link tapCause} for peeking at the full failure cause * @see {@link catchDefect} for recovering from defects * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5091,6 +5239,7 @@ export const tapDefect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5165,6 +5314,7 @@ export const catchIf: { * @see {@link catchTags} for `_tag` based recovery from multiple tagged errors * @see {@link catchCauseFilter} for filtering full causes * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5239,6 +5389,7 @@ export const catchFilter: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5356,6 +5507,7 @@ export const catchTag: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5480,6 +5632,7 @@ export const catchTags: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5615,6 +5768,7 @@ export const catchReason: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5761,6 +5915,7 @@ export const catchReasons: { * @see {@link catchReason} for recovering from a specific reason * @see {@link catchReasons} for handling several reasons at once * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5823,6 +5978,7 @@ export const unwrapReason: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5858,6 +6014,7 @@ export const mapError: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5906,6 +6063,7 @@ export const catchCauseIf: { * @see {@link catchFilter} for filtering typed error values instead of full causes * @see {@link catchCause} for recovering from every cause without filtering * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -5953,6 +6111,7 @@ export const catchCauseFilter: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -5993,6 +6152,7 @@ export const orElseIfEmpty: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -6029,6 +6189,7 @@ export const orElseSucceed: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -6081,6 +6242,7 @@ export const orDie = (self: Stream): Stream => fr * * @see {@link ignoreCause} for a variant that also ignores defects, not just typed failures * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -6130,6 +6292,7 @@ export const ignore: < * * @see {@link ignore} to ignore only typed failures without suppressing defects * + * @stability stable * @category error handling * @since 4.0.0 */ @@ -6181,6 +6344,7 @@ export const ignoreCause: < * await Effect.runPromise(program) * ``` * + * @stability stable * @category error handling * @since 2.0.0 */ @@ -6279,6 +6443,7 @@ const retryWithoutReset = ( * await Effect.runPromise(program) * ``` * + * @stability unstable * @category error handling * @since 3.16.0 */ @@ -6426,6 +6591,7 @@ export const withExecutionPlan: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6461,6 +6627,7 @@ export const take: { * await Effect.runPromise(program) // => [[1, 2]] * ``` * + * @stability unstable * @category filtering * @since 4.0.0 */ @@ -6521,6 +6688,7 @@ export const limitBytes: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6576,6 +6744,7 @@ export const takeRight: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6635,6 +6804,7 @@ export const takeUntil: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6697,6 +6867,7 @@ export const takeUntilEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6750,6 +6921,7 @@ export const takeWhile: { * @see {@link filterMap} for filtering across the whole stream instead of only the leading prefix * @see {@link dropWhileFilter} for dropping the accepted prefix and keeping the remaining original elements * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -6808,6 +6980,7 @@ export const takeWhileFilter: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -6853,6 +7026,7 @@ export const takeWhileEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6898,6 +7072,7 @@ export const drop: { * })) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6934,6 +7109,7 @@ export const dropUntil: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -6976,6 +7152,7 @@ export const dropUntilEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -7017,6 +7194,7 @@ export const dropWhile: { * @see {@link takeWhileFilter} for keeping the accepted prefix as filter success values * @see {@link dropWhileEffect} for effectful predicate prefix dropping * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -7058,6 +7236,7 @@ export const dropWhileFilter: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -7118,6 +7297,7 @@ export const dropWhileEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -7163,6 +7343,7 @@ export const dropRight: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -7197,6 +7378,7 @@ export const chunks = (self: Stream): Stream() * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 2.0.0 */ @@ -7846,6 +8036,7 @@ export const mapAccumEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -7915,6 +8106,7 @@ export const mapAccumArrayEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category accumulation * @since 2.0.0 */ @@ -7966,6 +8158,7 @@ export const scan: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category accumulation * @since 2.0.0 */ @@ -8010,6 +8203,7 @@ export const scanEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category rate limiting * @since 2.0.0 */ @@ -8125,6 +8319,7 @@ export const debounce: { * })) * ``` * + * @stability stable * @category rate limiting * @since 2.0.0 */ @@ -8288,6 +8483,7 @@ const throttleShapeEffect = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category rate limiting * @since 2.0.0 */ @@ -8352,6 +8548,7 @@ export const throttle: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -8389,6 +8586,7 @@ export const grouped: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -8437,6 +8635,7 @@ export const groupedWithin: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -8506,6 +8705,7 @@ export const groupBy: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -8586,7 +8786,7 @@ const groupByImpl = ( Queue.make({ capacity: options?.bufferSize ?? 4096 }).pipe( Effect.tap((queue) => { MutableHashMap.set(queueMap, key, queue) - return Queue.offer(out, [key, fromQueue(queue)]) + return Queue.offer(out, [key, fromQueueShutdownOnEnd(queue)]) }) ), (queue) => { @@ -8628,6 +8828,7 @@ const groupByImpl = ( * @see {@link groupByKey} for grouping all elements with the same key across the stream * @see {@link groupBy} for custom grouped stream construction * + * @stability stable * @category grouping * @since 2.0.0 */ @@ -8704,6 +8905,7 @@ export const groupAdjacentBy: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category aggregation * @since 2.0.0 */ @@ -8779,6 +8981,7 @@ export const transduce: { * })) * ``` * + * @stability stable * @category aggregation * @since 2.0.0 */ @@ -8832,6 +9035,7 @@ export const aggregate: { * })) * ``` * + * @stability stable * @category aggregation * @since 2.0.0 */ @@ -8961,6 +9165,7 @@ export const aggregateWithin: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category broadcasting * @since 4.0.0 */ @@ -9079,6 +9284,7 @@ const makePubSub = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category broadcasting * @since 2.0.0 */ @@ -9160,6 +9366,7 @@ export const broadcast: { * result // => { values: [[1, 2, 3], [1, 2, 3]], acquisitions: 1 } * ``` * + * @stability stable * @category broadcasting * @since 3.8.0 */ @@ -9241,6 +9448,7 @@ export const share: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -9288,6 +9496,7 @@ export const pipeThroughChannel: { * })) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -9328,6 +9537,7 @@ export const pipeThroughChannelOrFail: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category sequencing * @since 2.0.0 */ @@ -9362,6 +9572,7 @@ export const pipeThrough: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category accumulation * @since 4.0.0 */ @@ -9388,6 +9599,7 @@ export const collect = (self: Stream): Stream, E, R> * await Effect.runPromise(program) * ``` * + * @stability stable * @category accumulation * @since 2.0.0 */ @@ -9417,6 +9629,7 @@ export const accumulate = (self: Stream): Stream(self: Stream): Stream => chan * ) * ``` * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -9498,6 +9712,7 @@ export const changesWith: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category deduplication * @since 2.0.0 */ @@ -9571,6 +9786,7 @@ export const changesWithEffect: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category text * @since 2.0.0 */ @@ -9616,6 +9832,7 @@ export const decodeText: < * await Effect.runPromise(program) * ``` * + * @stability stable * @category text * @since 2.0.0 */ @@ -9641,6 +9858,7 @@ export const encodeText = (self: Stream): Stream(self: Stream): Stream ["success"] * ``` * + * @stability stable * @category resource management * @since 4.0.0 */ @@ -10017,6 +10242,7 @@ export const onExit: { * errors // => ["boom"] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -10055,6 +10281,7 @@ export const onError: { * events // => ["started"] * ``` * + * @stability stable * @category sequencing * @since 3.6.0 */ @@ -10089,6 +10316,7 @@ export const onStart: { * first // => [1] * ``` * + * @stability stable * @category sequencing * @since 4.0.0 */ @@ -10126,6 +10354,7 @@ export const onFirst: { * events // => ["ended"] * ``` * + * @stability stable * @category sequencing * @since 3.6.0 */ @@ -10164,6 +10393,7 @@ export const onEnd: { * events // => ["cleanup"] * ``` * + * @stability stable * @category resource management * @since 2.0.0 */ @@ -10202,6 +10432,7 @@ export const ensuring: { * await Effect.runPromise(Stream.runCollect(withEnv)) // => ["Hello, Ada"] * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -10260,6 +10491,7 @@ export const provide: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -10305,6 +10537,7 @@ export const provideContext: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -10357,6 +10590,7 @@ export const provideService: { * events // => ["loading"] * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -10414,6 +10648,7 @@ export const provideServiceEffect: { * ) * ``` * + * @stability stable * @category providing services * @since 4.0.0 */ @@ -10454,6 +10689,7 @@ export const updateContext: { * await Effect.runPromise(Effect.provideService(program, Counter, { count: 0 })) * ``` * + * @stability stable * @category providing services * @since 2.0.0 */ @@ -10499,6 +10735,7 @@ export const updateService: { * ) * ``` * + * @stability stable * @category tracing * @since 2.0.0 */ @@ -10538,6 +10775,7 @@ export const withSpan: { * await Effect.runPromise(effect) * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -10580,6 +10818,7 @@ export { * await Effect.runPromise(program) * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -10604,6 +10843,7 @@ export { * await Effect.runPromise(result) // => [{ a: 1, b: 2 }, { a: 2, b: 3 }] * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -10657,6 +10897,7 @@ export const bind: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -10705,6 +10946,7 @@ export const bindEffect: { * await Effect.runPromise(Stream.runCollect(stream)) // => [{ value: 1 }, { value: 2 }, { value: 3 }] * ``` * + * @stability stable * @category do notation * @since 2.0.0 */ @@ -10729,6 +10971,7 @@ export const bindTo: { * await Effect.runPromise(program) // => 6 * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -10769,6 +11012,7 @@ export const run: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -10802,6 +11046,7 @@ export const runCollect = (self: Stream): Effect.Effect(self: Stream): Effect.Effect(self: Stream): Effect.Effect(self: Stream): Effect.Effect(self: Stream): Effect.Effect ["Processing: 1", "Processing: 2", "Processing: 3"] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11060,6 +11311,7 @@ export const runForEach: { * values // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11112,6 +11364,7 @@ export const runForEachWhile: { * chunks // => ["1, 2, 3, 4, 5"] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11149,6 +11402,7 @@ export const runForEachArray: { * values // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11180,6 +11434,7 @@ export const runDrain = (self: Stream): Effect.Effect( * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11238,6 +11494,7 @@ export const mkString = (self: Stream): Effect.Effect [1, 2, 3, 4] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11266,6 +11523,7 @@ export const mkArrayBuffer = (self: Stream): Effect.Effe * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11295,6 +11553,7 @@ export const mkUint8Array = (self: Stream): Effect.Effec * values // => [ 1, 2, 3, 4, 5 ] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11372,6 +11631,7 @@ export const toReadableStreamWith: { * values // => [ 1, 2, 3 ] * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11420,6 +11680,7 @@ export const toReadableStream: { * await Effect.runPromise(effect) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11464,6 +11725,7 @@ export const toReadableStreamEffect: { * await Array.fromAsync(iterable) // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11573,6 +11835,7 @@ export const toAsyncIterableWith: { * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 3.15.0 */ @@ -11595,6 +11858,7 @@ export const toAsyncIterableEffect = (self: Stream): Effect.Ef * await Array.fromAsync(Stream.toAsyncIterable(stream)) // => [1, 2, 3] * ``` * + * @stability stable * @category destructors * @since 3.15.0 */ @@ -11630,6 +11894,7 @@ export const toAsyncIterable = (self: Stream): AsyncIterable => * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11680,6 +11945,7 @@ export const runIntoPubSub: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11754,6 +12020,7 @@ export const toPubSub: { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @category destructors * @since 4.0.0 */ @@ -11818,6 +12085,7 @@ export const toPubSubTake: { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ @@ -11882,6 +12150,7 @@ export const toQueue: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category destructors * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/String.ts b/packages/effect/src/String.ts similarity index 96% rename from repos/effect/packages/effect/src/String.ts rename to packages/effect/src/String.ts index dad0d3521..2391f1797 100644 --- a/repos/effect/packages/effect/src/String.ts +++ b/packages/effect/src/String.ts @@ -6,6 +6,7 @@ * padding, replacement, normalization, safe character access, search helpers * that return `Option`, and joining strings through a reducer. * + * @stability stable * @since 2.0.0 */ @@ -37,6 +38,7 @@ import * as Reducer from "./Reducer.ts" * * @see {@link isString} for checking whether a value is a primitive string * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -54,6 +56,7 @@ export const String = globalThis.String * String.isString(1) // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -73,6 +76,7 @@ export const isString: Refinement = predicate.isString * String.Order("apple", "apple") // => 0 * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -90,6 +94,7 @@ export const Order: order.Order = order.String * String.Equivalence("hello", "world") // => false * ``` * + * @stability stable * @category instances * @since 2.0.0 */ @@ -111,6 +116,7 @@ export const Equivalence: Equ.Equivalence = Equ.String * String.isEmpty(String.empty) // => true * ``` * + * @stability stable * @category constants * @since 2.0.0 */ @@ -130,6 +136,7 @@ export const empty: "" = "" as const * const witness: Result = "helloworld" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -147,6 +154,7 @@ export type Concat = `${A}${B}` * pipe("hello", String.concat("world")) // => "helloworld" * ``` * + * @stability stable * @category combining * @since 2.0.0 */ @@ -167,6 +175,7 @@ export const concat: { * String.toUpperCase("hello") // => "HELLO" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -184,6 +193,7 @@ export const toUpperCase = (self: S): Uppercase => self.toU * String.toLowerCase("HELLO") // => "hello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -201,6 +211,7 @@ export const toLowerCase = (self: T): Lowercase => self.toL * String.capitalize("hello") // => "Hello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -222,6 +233,7 @@ export const capitalize = (self: T): Capitalize => { * String.uncapitalize("Hello") // => "hello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -248,6 +260,7 @@ export const uncapitalize = (self: T): Uncapitalize => { * pipe("hello world", String.replace("world", "Effect")) // => "hello Effect" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -267,6 +280,7 @@ export const replace = (searchValue: string | RegExp, replaceValue: string) => ( * const witness: Result = "hello" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -284,6 +298,7 @@ export type Trim = TrimEnd> * String.trim(" hello world ") // => "hello world" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -302,6 +317,7 @@ export const trim = (self: A): Trim => self.trim() as Trim< * const witness: Result = "hello" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -319,6 +335,7 @@ export type TrimStart = A extends `${" " | "\n" | "\t" | "\r"} * String.trimStart(" hello world") // => "hello world" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -337,6 +354,7 @@ export const trimStart = (self: A): TrimStart => self.trimS * const witness: Result = "hello" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -354,6 +372,7 @@ export type TrimEnd = A extends `${infer B}${" " | "\n" | "\t" * String.trimEnd("hello world ") // => "hello world" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -371,6 +390,7 @@ export const trimEnd = (self: A): TrimEnd => self.trimEnd() * pipe("hello world", String.slice(0, 5)) // => "hello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -388,6 +408,7 @@ export const slice = (start?: number, end?: number) => (self: string): string => * String.isEmpty("a") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ @@ -405,6 +426,7 @@ export const isEmpty = (self: string): self is "" => self.length === 0 * String.isNonEmpty("a") // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -421,6 +443,7 @@ export const isNonEmpty = (self: string): boolean => self.length > 0 * String.length("abc") // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -439,6 +462,7 @@ export const length = (self: string): number => self.length * String.split("hello,world", ",") // => ["hello", "world"] * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -463,6 +487,7 @@ export const split: { * pipe("hello world", String.includes("foo")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -481,6 +506,7 @@ export const includes = (searchString: string, position?: number) => (self: stri * pipe("hello world", String.startsWith("world")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -499,6 +525,7 @@ export const startsWith = (searchString: string, position?: number) => (self: st * pipe("hello world", String.endsWith("hello")) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -517,6 +544,7 @@ export const endsWith = (searchString: string, position?: number) => (self: stri * String.charCodeAt("abc", 4) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -541,6 +569,7 @@ export const charCodeAt: { * pipe("abcd", String.substring(1, 3)) // => "bc" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -558,6 +587,7 @@ export const substring = (start: number, end?: number) => (self: string): string * pipe("abc", String.at(4)) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -578,6 +608,7 @@ export const at: { * pipe("abc", String.charAt(4)) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -601,6 +632,7 @@ export const charAt: { * pipe("abc", String.codePointAt(10)) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -621,6 +653,7 @@ export const codePointAt: { * pipe("abbbc", String.indexOf("z")) // => Option.none() * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -639,6 +672,7 @@ export const indexOf = (searchString: string) => (self: string): Option.Option Option.none() * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -660,6 +694,7 @@ export const lastIndexOf = (searchString: string) => (self: string): Option.Opti * pipe("a", String.localeCompare("a")) // => 0 * ``` * + * @stability stable * @category comparisons * @since 2.0.0 */ @@ -684,6 +719,7 @@ export const localeCompare = * pipe("hello", String.match(/x/)) // => Option.none() * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -704,6 +740,7 @@ export const match = (regExp: RegExp | string) => (self: string): Option.Option< * Array.from(matches, (match) => [match[0], match.index]) // => [["l", 2], ["l", 3], ["l", 9]] * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -731,6 +768,7 @@ export const matchAll = (regExp: RegExp) => (self: string): IterableIterator [0x73, 0x323, 0x307] * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -748,6 +786,7 @@ export const normalize = (form?: "NFC" | "NFD" | "NFKC" | "NFKD") => (self: stri * pipe("a", String.padEnd(5, "_")) // => "a____" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -766,6 +805,7 @@ export const padEnd = (maxLength: number, fillString?: string) => (self: string) * pipe("a", String.padStart(5, "_")) // => "____a" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -788,6 +828,7 @@ export const padStart = (maxLength: number, fillString?: string) => (self: strin * pipe("hello", String.repeat(3)) // => "hellohellohello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -805,6 +846,7 @@ export const repeat = (count: number) => (self: string): string => self.repeat(C * pipe("ababb", String.replaceAll(/ba/g, "cc")) // => "accbb" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -825,6 +867,7 @@ export const replaceAll = (searchValue: string | RegExp, replaceValue: string) = * String.search("ababb", "d") // => Option.none() * ``` * + * @stability stable * @category searching * @since 2.0.0 */ @@ -849,6 +892,7 @@ export const search: { * pipe(str, String.toLocaleLowerCase("tr")) // => "i" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -867,6 +911,7 @@ export const toLocaleLowerCase = (locale?: string | Array) => (self: str * pipe(str, String.toLocaleUpperCase("lt-LT")) // => "I" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -894,6 +939,7 @@ export const toLocaleUpperCase = (locale?: string | Array) => (self: str * String.takeLeft("Hello World", 5) // => "Hello" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -923,6 +969,7 @@ export const takeLeft: { * String.takeRight("Hello World", 5) // => "World" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -949,6 +996,7 @@ const LF = 0x0a * Array.from(String.linesIterator("hello\nworld\n")) // => ["hello", "world"] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -966,6 +1014,7 @@ export const linesIterator = (self: string): LinesIterator => linesSeparated(sel * Array.from(String.linesWithSeparators("hello\nworld\n")) // => ["hello\n", "world\n"] * ``` * + * @stability stable * @category splitting * @since 2.0.0 */ @@ -983,6 +1032,7 @@ export const linesWithSeparators = (s: string): LinesIterator => linesSeparated( * String.stripMarginWith(" |hello\n |world", "|") // => "hello\nworld" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1020,6 +1070,7 @@ export const stripMarginWith: { * String.stripMargin(" |hello\n |world") // => "hello\nworld" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1037,6 +1088,7 @@ export const stripMargin = (self: string): string => stripMarginWith(self, "|") * String.snakeToCamel("foo_bar_baz") // => "fooBarBaz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1061,6 +1113,7 @@ export const snakeToCamel = (self: string): string => { * String.snakeToPascal("foo_bar_baz") // => "FooBarBaz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1085,6 +1138,7 @@ export const snakeToPascal = (self: string): string => { * String.snakeToKebab("foo_bar_baz") // => "foo-bar-baz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1102,6 +1156,7 @@ export const snakeToKebab = (self: string): string => self.replace(/_/g, "-") * String.camelToSnake("fooBarBaz") // => "foo_bar_baz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1119,6 +1174,7 @@ export const camelToSnake = (self: string): string => self.replace(/([A-Z])/g, " * String.pascalToSnake("FooBarBaz") // => "foo_bar_baz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1137,6 +1193,7 @@ export const pascalToSnake = (self: string): string => * String.kebabToSnake("foo-bar-baz") // => "foo_bar_baz" * ``` * + * @stability stable * @category transforming * @since 2.0.0 */ @@ -1221,6 +1278,7 @@ const linesSeparated = (self: string, stripped: boolean): LinesIterator => new L * @see {@link kebabCase} for fixed lowercase hyphen-separated output * @see {@link snakeCase} for fixed lowercase underscore-separated output * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1309,6 +1367,7 @@ const pascalCaseTransform = (input: string): string => { * @see {@link noCase} for configurable delimiters and part transforms * @see {@link snakeToPascal} for converting known snake_case input only * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1336,6 +1395,7 @@ const camelCaseTransform = (input: string, index: number): string => * @see {@link kebabCase} for lowercase hyphen-separated output * @see {@link constantCase} for uppercase underscore-separated output * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1359,6 +1419,7 @@ export const camelCase: (self: string) => string = noCase({ * @see {@link configCase} for configuration key casing that preserves numeric word groups * @see {@link noCase} for configurable delimiters and part transforms * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1382,6 +1443,7 @@ export const constantCase: (self: string) => string = noCase({ * example, `"api-v2 xml"` becomes `"API_V2_XML"`. * * @see {@link constantCase} for standard uppercase underscore-separated output + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1402,6 +1464,7 @@ export const configCase: (self: string) => string = (self) => * @see {@link camelCase} for lower-initial camelCase output * @see {@link pascalCase} for upper-initial PascalCase output * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1421,6 +1484,7 @@ export const kebabCase: (self: string) => string = noCase({ * @see {@link kebabCase} for lowercase hyphen-separated output * @see {@link constantCase} for uppercase underscore-separated output * + * @stability stable * @category transforming * @since 4.0.0 */ @@ -1441,6 +1505,7 @@ export const snakeCase: (self: string) => string = noCase({ * * @see {@link concat} for concatenating two strings directly * + * @stability stable * @category combining * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Struct.ts b/packages/effect/src/Struct.ts similarity index 98% rename from repos/effect/packages/effect/src/Struct.ts rename to packages/effect/src/Struct.ts index b7ada252a..0125a79b3 100644 --- a/repos/effect/packages/effect/src/Struct.ts +++ b/packages/effect/src/Struct.ts @@ -8,6 +8,7 @@ * list of keys. The module also includes type-level helpers for simplifying and * merging object shapes. * + * @stability stable * @since 2.0.0 */ @@ -46,6 +47,7 @@ import * as Reducer from "./Reducer.ts" * * @see {@link Mutable} – also flattens but removes `readonly` * @see {@link Assign} – merges two types with right-side precedence + * @stability stable * @category utility types * @since 4.0.0 */ @@ -78,6 +80,7 @@ export type Simplify = { [K in keyof T]: T[K] } & {} * ``` * * @see {@link Simplify} – flattens intersections without removing `readonly` + * @stability stable * @category utility types * @since 4.0.0 */ @@ -111,6 +114,7 @@ export type Mutable = { -readonly [K in keyof T]: T[K] } & {} * * @see {@link assign} – the runtime equivalent * @see {@link Simplify} – flatten the resulting intersection + * @stability stable * @category utility types * @since 4.0.0 */ @@ -137,6 +141,7 @@ export type Assign = Simplify(self: S): Array<(keyof S) & string> => * * @see {@link omit} – the inverse (exclude keys instead) * @see {@link get} – extract a single value + * @stability stable * @category filtering * @since 2.0.0 */ @@ -234,6 +241,7 @@ export const pick: { * ``` * * @see {@link pick} – the inverse (keep only specified keys) + * @stability stable * @category filtering * @since 2.0.0 */ @@ -273,6 +281,7 @@ export const omit: { * * @see {@link Assign} – the type-level equivalent * @see {@link evolve} – transform individual values instead of replacing them + * @stability stable * @category combining * @since 4.0.0 */ @@ -323,6 +332,7 @@ type Evolved = Simplify< * @see {@link evolveKeys} – transform keys instead of values * @see {@link evolveEntries} – transform both keys and values * @see {@link map} – apply the same transformation to all values + * @stability stable * @category transforming * @since 2.0.0 */ @@ -372,6 +382,7 @@ type KeyEvolved = Simplify< * @see {@link renameKeys} – rename keys with a static mapping * @see {@link evolve} – transform values instead of keys * @see {@link evolveEntries} – transform both keys and values + * @stability stable * @category transforming * @since 4.0.0 */ @@ -427,6 +438,7 @@ type EntryEvolved = { * * @see {@link evolve} – transform values only * @see {@link evolveKeys} – transform keys only + * @stability stable * @category transforming * @since 4.0.0 */ @@ -466,6 +478,7 @@ export const evolveEntries: { * * @see {@link evolveKeys} – rename keys using functions * @see {@link evolveEntries} – rename keys and transform values + * @stability stable * @category transforming * @since 4.0.0 */ @@ -512,6 +525,7 @@ export const renameKeys: { * ``` * * @see {@link makeOrder} – create an `Order` for structs + * @stability stable * @category instances * @since 4.0.0 */ @@ -546,6 +560,7 @@ export const makeEquivalence = Equivalence.Struct * ``` * * @see {@link makeEquivalence} – create an `Equivalence` for structs + * @stability stable * @category ordering * @since 4.0.0 */ @@ -581,6 +596,7 @@ export const makeOrder = order.Struct * @see {@link Apply} – apply a Lambda to a concrete type * @see {@link lambda} – create a runtime lambda value * @see {@link map} – use a lambda to transform all struct values + * @stability stable * @category utility types * @since 4.0.0 */ @@ -619,6 +635,7 @@ export interface Lambda { * ``` * * @see {@link Lambda} – the base interface + * @stability stable * @category utility types * @since 4.0.0 */ @@ -657,6 +674,7 @@ export type Apply = (L & { readonly "~lambda.in": V })["~la * * @see {@link Lambda} – the type-level interface * @see {@link map} – apply a lambda to all struct values + * @stability stable * @category constructors * @since 4.0.0 */ @@ -694,6 +712,7 @@ export const lambda = any>( * @see {@link mapPick} – apply a lambda only to selected keys * @see {@link mapOmit} – apply a lambda to all keys except selected ones * @see {@link evolve} – apply different functions to different keys + * @stability stable * @category mapping * @since 4.0.0 */ @@ -740,6 +759,7 @@ export const map: { * * @see {@link map} – apply a lambda to all keys * @see {@link mapOmit} – apply a lambda to all keys except selected ones + * @stability stable * @category mapping * @since 4.0.0 */ @@ -794,6 +814,7 @@ export const mapPick: { * * @see {@link map} – apply a lambda to all keys * @see {@link mapPick} – apply a lambda only to selected keys + * @stability stable * @category mapping * @since 4.0.0 */ @@ -881,6 +902,7 @@ function buildStruct< * ``` * * @see {@link makeReducer} – like `makeCombiner` but with an initial value + * @stability stable * @category combining * @since 4.0.0 */ @@ -938,6 +960,7 @@ export function makeCombiner( * ``` * * @see {@link makeCombiner} – like `makeReducer` but without an initial value + * @stability stable * @category folding * @since 4.0.0 */ @@ -972,6 +995,7 @@ export function makeReducer( * Struct.Record(["a", "b"], "value") // => { a: "value", b: "value" } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/SubscriptionRef.ts b/packages/effect/src/SubscriptionRef.ts similarity index 98% rename from repos/effect/packages/effect/src/SubscriptionRef.ts rename to packages/effect/src/SubscriptionRef.ts index 1a4adf732..4812904a0 100644 --- a/repos/effect/packages/effect/src/SubscriptionRef.ts +++ b/packages/effect/src/SubscriptionRef.ts @@ -7,6 +7,7 @@ * module includes constructors, current-value reads, the `changes` stream, * writes, updates, partial updates, and effectful update helpers. * + * @stability stable * @since 2.0.0 */ import * as Effect from "./Effect.ts" @@ -31,6 +32,7 @@ const TypeId = "~effect/SubscriptionRef" * Use to observe the current value and subsequent updates as a * stream. * + * @stability stable * @category models * @since 2.0.0 */ @@ -48,6 +50,7 @@ export interface SubscriptionRef extends SubscriptionRef.Variance, * Use to narrow an unknown value before calling `SubscriptionRef` operations * that require a subscription reference. * + * @stability stable * @category guards * @since 4.0.0 */ @@ -59,6 +62,7 @@ export const isSubscriptionRef: (u: unknown) => u is SubscriptionRef = * The `SubscriptionRef` namespace containing type definitions associated with * subscription references. * + * @stability stable * @since 2.0.0 */ export declare namespace SubscriptionRef { @@ -105,6 +109,7 @@ const Proto = { * @see {@link changes} for streaming the current value and subsequent updates * @see {@link set} for replacing the value and notifying subscribers * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -154,6 +159,7 @@ export const make = (value: A): Effect.Effect> => * await Effect.runPromise(program) // => [0, 1, 2] * ``` * + * @stability stable * @category subscriptions * @since 4.0.0 */ @@ -187,6 +193,7 @@ export const changes = (self: SubscriptionRef): Stream.Stream => Stream * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -209,6 +216,7 @@ export const getUnsafe = (self: SubscriptionRef): A => self.value * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -234,6 +242,7 @@ export const get = (self: SubscriptionRef): Effect.Effect => Effect.syn * await Effect.runPromise(program) // => [10, 20] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -272,6 +281,7 @@ const setUnsafe = (self: SubscriptionRef, value: A) => { * await Effect.runPromise(program) // => [10, 20] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -309,6 +319,7 @@ export const getAndUpdate: { * await Effect.runPromise(program) // => [10, 15] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -360,6 +371,7 @@ export const getAndUpdateEffect: { * await Effect.runPromise(program) // => [10, 20] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -413,6 +425,7 @@ export const getAndUpdateSome: { * await Effect.runPromise(program) // => [10, 13] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -460,6 +473,7 @@ export const getAndUpdateSomeEffect: { * await Effect.runPromise(program) // => ["Old value was 10", 20] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -500,6 +514,7 @@ export const modify: { * await Effect.runPromise(program) // => ["Doubled from 10", 20] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -556,6 +571,7 @@ export const modifyEffect: { * await Effect.runPromise(program) // => ["Updated", 20] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -616,6 +632,7 @@ export const modifySome: { * await Effect.runPromise(program) // => ["Updated", 15] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -659,6 +676,7 @@ export const modifySomeEffect: { * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -688,6 +706,7 @@ export const set: { * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -720,6 +739,7 @@ export const setAndGet: { * await Effect.runPromise(program) // => 20 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -752,6 +772,7 @@ export const update: { * await Effect.runPromise(program) // => 15 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -784,6 +805,7 @@ export const updateEffect: { * await Effect.runPromise(program) // => 20 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -819,6 +841,7 @@ export const updateAndGet: { * await Effect.runPromise(program) // => 15 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -860,6 +883,7 @@ export const updateAndGetEffect: { * await Effect.runPromise(program) // => 20 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -909,6 +933,7 @@ export const updateSome: { * await Effect.runPromise(program) // => 13 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -962,6 +987,7 @@ export const updateSomeEffect: { * await Effect.runPromise(program) // => 20 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -1011,6 +1037,7 @@ export const updateSomeAndGet: { * await Effect.runPromise(program) // => 13 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Symbol.ts b/packages/effect/src/Symbol.ts similarity index 95% rename from repos/effect/packages/effect/src/Symbol.ts rename to packages/effect/src/Symbol.ts index de444c25d..2df931e76 100644 --- a/repos/effect/packages/effect/src/Symbol.ts +++ b/packages/effect/src/Symbol.ts @@ -4,6 +4,7 @@ * and must be narrowed before it can be used as a symbol key, identifier, or * discriminant. * + * @stability stable * @since 2.0.0 */ @@ -25,6 +26,7 @@ import * as predicate from "./Predicate.ts" * Symbol.isSymbol("a") // => false * ``` * + * @stability stable * @category guards * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/SynchronizedRef.ts b/packages/effect/src/SynchronizedRef.ts similarity index 97% rename from repos/effect/packages/effect/src/SynchronizedRef.ts rename to packages/effect/src/SynchronizedRef.ts index 4db44c41b..6c48dce84 100644 --- a/repos/effect/packages/effect/src/SynchronizedRef.ts +++ b/packages/effect/src/SynchronizedRef.ts @@ -8,6 +8,7 @@ * from concurrent updates. This module includes constructors, reads, writes, * updates, partial updates, and effectful update helpers. * + * @stability stable * @since 2.0.0 */ import * as Effect from "./Effect.ts" @@ -32,6 +33,7 @@ const TypeId = "~effect/SynchronizedRef" * * @see {@link Ref.Ref} for a plain `Ref` when updates do not need effectful synchronization * + * @stability stable * @category models * @since 2.0.0 */ @@ -60,6 +62,7 @@ const Proto = { * Use when you need synchronous `SynchronizedRef` construction outside an * Effect workflow. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -86,6 +89,7 @@ export const makeUnsafe = (value: A): SynchronizedRef => { * @see {@link makeUnsafe} for synchronous construction when the caller controls safe initialization * @see {@link Ref.make} for a plain `Ref` when updates do not need effectful synchronization * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -102,6 +106,7 @@ export const make = (value: A): Effect.Effect> => Effect.s * * @see {@link get} for the Effect-wrapped read when composing inside Effect programs * + * @stability stable * @category getters * @since 4.0.0 */ @@ -117,6 +122,7 @@ export const getUnsafe = (self: SynchronizedRef): A => self.backing.ref.cu * * @see {@link getUnsafe} for synchronous reads when the caller controls safe access outside `Effect` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -135,6 +141,7 @@ export const get = (self: SynchronizedRef): Effect.Effect => Effect.syn * @see {@link setAndGet} for setting a value and returning the new value * @see {@link getAndUpdate} for deriving the new value from the current value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -160,6 +167,7 @@ export const getAndSet: { * @see {@link updateAndGet} for updating and returning the new value * @see {@link getAndUpdateEffect} for effectful updates that return the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -187,6 +195,7 @@ export const getAndUpdate: { * @see {@link modifyEffect} for effectful updates with a custom return value * @see {@link getAndUpdateSomeEffect} for conditional effectful updates that return the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -218,6 +227,7 @@ export const getAndUpdateEffect: { * @see {@link getAndUpdate} for always applying a pure update * @see {@link updateSome} for applying a pure conditional update without returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -243,6 +253,7 @@ export const getAndUpdateSome: { * @see {@link getAndUpdateSome} for the pure conditional variant * @see {@link updateSomeEffect} for effectful conditional updates without returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -277,6 +288,7 @@ export const getAndUpdateSomeEffect: { * @see {@link modifySome} for deriving a result and optionally updating the stored value * @see {@link updateAndGet} for returning the new stored value instead of a separate result * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -301,6 +313,7 @@ export const modify: { * @see {@link modify} for the pure variant * @see {@link updateEffect} for effectfully storing a new value without a separate result * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -331,6 +344,7 @@ export const modifyEffect: { * @see {@link modify} for always storing a new value * @see {@link updateSome} for optional updates without a separate return value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -363,6 +377,7 @@ export const modifySome: { * @see {@link modifySome} for the pure variant * @see {@link updateSomeEffect} for effectful optional updates without a separate return value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -404,6 +419,7 @@ export const modifySomeEffect: { * @see {@link setAndGet} for replacing the value when the new value should be returned * @see {@link update} for deriving the next value from the current value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -427,6 +443,7 @@ export const set: { * @see {@link set} for setting without returning a value * @see {@link getAndSet} for setting while returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -452,6 +469,7 @@ export const setAndGet: { * @see {@link updateAndGet} for returning the new stored value * @see {@link getAndUpdate} for returning the previous stored value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -479,6 +497,7 @@ export const update: { * @see {@link modifyEffect} for returning a separate result while storing a new value * @see {@link updateSomeEffect} for effectfully applying only some state transitions * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -508,6 +527,7 @@ export const updateEffect: { * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for updating while returning the previous value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -532,6 +552,7 @@ export const updateAndGet: { * @see {@link updateEffect} for effectful updates without returning the new value * @see {@link updateAndGet} for the pure variant * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -562,6 +583,7 @@ export const updateAndGetEffect: { * @see {@link update} for always applying a pure update * @see {@link updateSomeAndGet} for returning the resulting current value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -586,6 +608,7 @@ export const updateSome: { * @see {@link updateSome} for the pure conditional variant * @see {@link updateEffect} for effectful updates that always store a new value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -621,6 +644,7 @@ export const updateSomeEffect: { * @see {@link updateSome} for conditional updates without returning a value * @see {@link updateAndGet} for always applying a pure update and returning the new value * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -646,6 +670,7 @@ export const updateSomeAndGet: { * @see {@link updateSomeEffect} for effectful conditional updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that always store and return a new value * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Take.ts b/packages/effect/src/Take.ts similarity index 96% rename from repos/effect/packages/effect/src/Take.ts rename to packages/effect/src/Take.ts index 48c2dd796..098d06253 100644 --- a/repos/effect/packages/effect/src/Take.ts +++ b/packages/effect/src/Take.ts @@ -4,6 +4,7 @@ * emitted values, a failed `Exit`, or a successful `Exit` carrying the * completion value. * + * @stability stable * @since 2.0.0 */ import type { NonEmptyReadonlyArray } from "./Array.ts" @@ -23,6 +24,7 @@ import type * as Pull from "./Pull.ts" * * @see {@link toPull} for interpreting a `Take` as a `Pull` step * + * @stability stable * @category models * @since 2.0.0 */ @@ -37,6 +39,7 @@ export type Take = NonEmptyReadonlyArray | Exit.Ex * Use to interpret a stored or transferred `Take` as a `Pull` step while * preserving emitted batches, ordinary failures, and completion values. * + * @stability stable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Terminal.ts b/packages/effect/src/Terminal.ts similarity index 96% rename from repos/effect/packages/effect/src/Terminal.ts rename to packages/effect/src/Terminal.ts index bbd046c52..3c5f5f24c 100644 --- a/repos/effect/packages/effect/src/Terminal.ts +++ b/packages/effect/src/Terminal.ts @@ -7,6 +7,7 @@ * the `QuitError` used when a user cancels input, a guard for that error, and a * constructor for custom terminal service implementations. * + * @stability unstable * @since 4.0.0 */ import type * as Cause from "./Cause.ts" @@ -25,6 +26,7 @@ const TypeId = "~effect/Terminal" * A `Terminal` represents a command-line interface which can read input from a * user and display messages to a user. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -58,6 +60,7 @@ export interface Terminal { * Keyboard key metadata for terminal input, including the key name and * modifier state. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -91,6 +94,7 @@ export interface Key { * * @see {@link Key} for the parsed key metadata stored on each input event * + * @stability unstable * @category models * @since 4.0.0 */ @@ -118,6 +122,7 @@ const QuitErrorTypeId = "~effect/Terminal/QuitError" * * @see {@link isQuitError} for checking unknown errors when handling terminal cancellation * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -147,6 +152,7 @@ export class QuitError extends Schema.Error("QuitError")({ * * @see {@link QuitError} for the error value produced when terminal input is quit * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -160,6 +166,7 @@ export const isQuitError = (u: unknown): u is QuitError => Predicate.hasProperty * Use to access or provide platform terminal capabilities such as reading * input, writing output, and inspecting terminal dimensions. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -180,6 +187,7 @@ export const Terminal: Context.Service = Context.Service("ef * `readLine`, and `display`; `make` attaches the `Terminal` service marker so * the result can be provided through the `Terminal` context service. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Tracer.ts b/packages/effect/src/Tracer.ts similarity index 97% rename from repos/effect/packages/effect/src/Tracer.ts rename to packages/effect/src/Tracer.ts index 024c10568..0858f19d5 100644 --- a/repos/effect/packages/effect/src/Tracer.ts +++ b/packages/effect/src/Tracer.ts @@ -7,6 +7,7 @@ * external span support, trace propagation settings, and the default in-memory * span implementation. * + * @stability stable * @since 2.0.0 */ import * as Context from "./Context.ts" @@ -23,6 +24,7 @@ import * as Option from "./Option.ts" * `span` to allocate a span from the supplied name, parent, annotations, * links, start time, kind, root flag, and sampling decision. * + * @stability stable * @category services * @since 2.0.0 */ @@ -48,6 +50,7 @@ const evaluate = "~effect/Effect/evaluate" satisfies core.evaluate * A low-level Effect primitive that can be evaluated by a tracer-specific * context for the current fiber. * + * @stability stable * @category models * @since 4.0.0 */ @@ -85,6 +88,7 @@ export interface EffectPrimitive { * endedStatus.endTime - endedStatus.startTime // => 500_000_000n * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -119,6 +123,7 @@ export type SpanStatus = { * await Effect.runPromise(getSpanIds(externalSpan)) // => ["span-123", "trace-456"] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -141,6 +146,7 @@ export type AnySpan = Span | ExternalSpan * Tracer.ParentSpanKey // => "effect/Tracer/ParentSpan" * ``` * + * @stability stable * @category constants * @since 4.0.0 */ @@ -165,6 +171,7 @@ export const ParentSpanKey = "effect/Tracer/ParentSpan" * await Effect.runPromise(Effect.provideService(program, Tracer.ParentSpan, parent)) // => "span-123" * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -193,6 +200,7 @@ export class ParentSpan extends Context.Service()(ParentSpa * externalSpan.spanId // => "span-abc-123" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -240,6 +248,7 @@ export interface ExternalSpan { * spans[0]?.status._tag // => "Ended" * ``` * + * @stability stable * @category options * @since 3.1.0 */ @@ -250,6 +259,7 @@ export interface SpanOptions extends SpanOptionsNoTrace, TraceOptions {} * attributes, links, parent or root selection, annotations, span kind, * sampling, and the trace level used for filtering. * + * @stability stable * @category options * @since 4.0.0 */ @@ -268,6 +278,7 @@ export interface SpanOptionsNoTrace { * Options that control stack trace capture for tracing wrappers. * `captureStackTrace` can disable capture or provide a lazy stack string. * + * @stability stable * @category options * @since 4.0.0 */ @@ -304,6 +315,7 @@ export interface TraceOptions { * spans[0]?.kind // => "server" * ``` * + * @stability stable * @category models * @since 3.1.0 */ @@ -366,6 +378,7 @@ export type SpanKind = "internal" | "server" | "client" | "producer" | "consumer * events // => [["loaded", 1_250_000_000n, { "cache.hit": true }]] * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -425,6 +438,7 @@ export interface Span { * spans[0]?.links[0]?.attributes["link.type"] // => "follows-from" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -449,6 +463,7 @@ export interface SpanLink { * * @see {@link Span} for the span values returned by tracer implementations * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -490,6 +505,7 @@ export const make = (options: Tracer): Tracer => options * spans.map((span) => Option.getOrUndefined(span.parent)?.spanId) // => ["span-abc-123"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -534,6 +550,7 @@ export const externalSpan = ( * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category services * @since 3.12.0 */ @@ -557,6 +574,7 @@ export const DisablePropagation = Context.Reference( * * @see {@link MinimumTraceLevel} for the threshold that decides whether spans at that level are sampled * + * @stability stable * @category services * @since 4.0.0 */ @@ -586,6 +604,7 @@ export const CurrentTraceLevel: Context.Reference = Context.Reference< * * @see {@link CurrentTraceLevel} for the default span level used when options do not specify one * + * @stability stable * @category services * @since 4.0.0 */ @@ -601,6 +620,7 @@ export const MinimumTraceLevel = Context.Reference< * Use when you need the raw context key for active tracer lookup in lower-level * tracing code. * + * @stability stable * @category constants * @since 4.0.0 */ @@ -626,6 +646,7 @@ export const TracerKey = "effect/Tracer" * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category services * @since 2.0.0 */ @@ -644,6 +665,7 @@ export const Tracer: Context.Reference = Context.Reference(Trace * that no tracing backend is installed and skip work that only a backend could * observe, such as recording span attributes. * + * @stability stable * @category references * @since 4.0.0 */ @@ -666,6 +688,7 @@ export const nativeTracer: Tracer = make({ * * @see {@link Span} for the interface implemented by native spans * + * @stability stable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Trie.ts b/packages/effect/src/Trie.ts similarity index 97% rename from repos/effect/packages/effect/src/Trie.ts rename to packages/effect/src/Trie.ts index c9203a880..78d30100d 100644 --- a/repos/effect/packages/effect/src/Trie.ts +++ b/packages/effect/src/Trie.ts @@ -7,6 +7,7 @@ * includes exact lookup, prefix lookup, longest-prefix lookup, iteration, * mapping, filtering, reducing, and traversal helpers. * + * @stability stable * @since 2.0.0 */ import type { Equal } from "./Equal.ts" @@ -54,6 +55,7 @@ const TypeId = TR.TrieTypeId * Trie.size(trie) // => 4 * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -77,6 +79,7 @@ export interface Trie extends Iterable<[string, Value]>, Equal, Pi * Array.from(trie) // => [] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -101,6 +104,7 @@ export const empty: () => Trie = TR.empty * trie // => Trie.make(["call", 0], ["me", 1], ["mind", 2], ["mid", 3]) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -120,6 +124,7 @@ export const fromIterable: (entries: Iterable) => Trie< * trie // => Trie.fromIterable([["ca", 0], ["me", 1]]) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -148,6 +153,7 @@ export const make: >( * Array.from(trie4) // => [["call", 0], ["me", 1], ["mid", 3], ["mind", 2]] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -177,6 +183,7 @@ export const insert: { * Array.from(Trie.keys(trie)) // => ["abc", "bca", "cab"] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -203,6 +210,7 @@ export const keys: (self: Trie) => IterableIterator = TR.keys * Array.from(Trie.values(trie)) // => [2, 0, 1] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -228,6 +236,7 @@ export const values: (self: Trie) => IterableIterator = TR.values * Array.from(Trie.entries(trie)) // => [["call", 0], ["me", 1]] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -252,6 +261,7 @@ export const entries: (self: Trie) => IterableIterator<[string, V]> = TR.e * Trie.toEntries(trie) // => [["call", 0], ["me", 1]] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -276,6 +286,7 @@ export const toEntries = (self: Trie): Array<[string, V]> => Array.from(en * Array.from(Trie.keysWithPrefix(trie, "she")) // => ["she", "shells"] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -303,6 +314,7 @@ export const keysWithPrefix: { * Array.from(Trie.valuesWithPrefix(trie, "she")) // => [0, 1] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -330,6 +342,7 @@ export const valuesWithPrefix: { * Array.from(Trie.entriesWithPrefix(trie, "she")) // => [["she", 0], ["shells", 1]] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -357,6 +370,7 @@ export const entriesWithPrefix: { * Trie.toEntriesWithPrefix(trie, "she") // => [["she", 3], ["shells", 0]] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -384,6 +398,7 @@ export const toEntriesWithPrefix: { * Trie.longestPrefixOf(trie, "sells") // => Option.some(["sells", 1]) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -408,6 +423,7 @@ export const longestPrefixOf: { * Trie.size(trie) // => 2 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -438,6 +454,7 @@ export const size: (self: Trie) => number = TR.size * Trie.get(trie, "mea") // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -471,6 +488,7 @@ export const get: { * Trie.has(trie, "mea") // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -494,6 +512,7 @@ export const has: { * Trie.isEmpty(trie1) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -528,6 +547,7 @@ export const isEmpty: (self: Trie) => boolean = TR.isEmpty * }) // => Result.fail("Expected trie to contain key") * ``` * + * @stability stable * @category unsafe * @since 4.0.0 */ @@ -559,6 +579,7 @@ export const getUnsafe: { * Trie.get(trie2, "call") // => Option.none() * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -586,6 +607,7 @@ export const remove: { * trie.pipe(Trie.reduce("", (acc, _, key) => acc + key)) // => "sellssheshells" * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -612,6 +634,7 @@ export const reduce: { * Trie.map(trie, (_, k) => k.length) // => Trie.make(["shells", 6], ["sells", 5], ["she", 3]) * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -638,6 +661,7 @@ export const map: { * Trie.filter(trie, (_, k) => k.length > 3) // => Trie.make(["shells", 0], ["sells", 1]) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -670,6 +694,7 @@ export const filter: { * ) // => Trie.make(["shells", 0], ["sells", 1]) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -695,6 +720,7 @@ export const filterMap: { * Trie.compact(trie) // => Trie.make(["shells", 0], ["she", 2]) * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -722,6 +748,7 @@ export const compact: (self: Trie>) => Trie = TR.compact * value // => 17 * ``` * + * @stability stable * @category traversing * @since 2.0.0 */ @@ -748,6 +775,7 @@ export const forEach: { * trie.pipe(Trie.modify("me", (v) => v)) // => trie * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -773,6 +801,7 @@ export const modify: { * trie.pipe(Trie.removeMany(["she", "sells"])) // => Trie.make(["shells", 0]) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -798,6 +827,7 @@ export const removeMany: { * ) // => Trie.make(["shells", 0], ["sells", 1], ["she", 2]) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Tuple.ts b/packages/effect/src/Tuple.ts similarity index 98% rename from repos/effect/packages/effect/src/Tuple.ts rename to packages/effect/src/Tuple.ts index ecaeefbc6..9e9d5f21c 100644 --- a/repos/effect/packages/effect/src/Tuple.ts +++ b/packages/effect/src/Tuple.ts @@ -8,6 +8,7 @@ * typed positions, and deriving comparison or combination helpers for tuple * shapes. * + * @stability stable * @since 2.0.0 */ import * as Combiner from "./Combiner.ts" @@ -40,6 +41,7 @@ import type { Apply, Lambda } from "./Struct.ts" * * @see {@link get} – access a single element by index * @see {@link appendElement} – append an element to a tuple + * @stability stable * @category constructors * @since 2.0.0 */ @@ -69,6 +71,7 @@ type Indices> = Exclude["length"], T * * @see {@link make} – create a tuple * @see {@link pick} – extract multiple elements into a new tuple + * @stability stable * @category getters * @since 4.0.0 */ @@ -116,6 +119,7 @@ type PickTuple, I extends ReadonlyArray, K> = _BuildTuple = { [I in keyof T]: I extends keyof E ? EvolveElement>( * ``` * * @see {@link makeCombiner} – like `makeReducer` but without an initial value + * @stability stable * @category folding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxChunk.ts b/packages/effect/src/TxChunk.ts similarity index 97% rename from repos/effect/packages/effect/src/TxChunk.ts rename to packages/effect/src/TxChunk.ts index 29342867a..e204c4b6f 100644 --- a/repos/effect/packages/effect/src/TxChunk.ts +++ b/packages/effect/src/TxChunk.ts @@ -8,6 +8,7 @@ * removing values, checking size, slicing, mapping, filtering, and combining * chunks. * + * @stability unstable * @since 4.0.0 */ import * as Chunk from "./Chunk.ts" @@ -66,6 +67,7 @@ const TypeId = "~effect/TxChunk" * await Effect.runPromise(program) // => [[1, 2, 3, 4], [0, 1, 2, 3, 4, 5]] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -118,6 +120,7 @@ const TxChunkProto = { * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -154,6 +157,7 @@ export const make = (initial: Chunk.Chunk): Effect.Effect> => * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -195,6 +199,7 @@ export const empty = (): Effect.Effect> => * await Effect.runPromise(program) // => [[1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -220,6 +225,7 @@ export const fromIterable = (iterable: Iterable): Effect.Effect * Chunk.toArray(await Effect.runPromise(TxChunk.get(txChunk))) // => [1, 2, 3] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -244,6 +250,7 @@ export const makeUnsafe = (ref: TxRef.TxRef>): TxChunk => { * TxChunk.isTxChunk(Chunk.empty()) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -278,6 +285,7 @@ export const isTxChunk = (u: unknown): u is TxChunk => hasProperty(u, T * await Effect.runPromise(program) // => [3, [1, 2, 3, 4]] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -323,6 +331,7 @@ export const modify: { * await Effect.runPromise(program) // => [3, 2, 1] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -356,6 +365,7 @@ export const update: { * await Effect.runPromise(program) // => [[1, 2, 3], 3] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -388,6 +398,7 @@ export const get = (self: TxChunk): Effect.Effect> => TxRef * await Effect.runPromise(program) // => [10, 20, 30, 40] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -425,6 +436,7 @@ export const set: { * await Effect.runPromise(program) // => [1, 2, 3, 4] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -462,6 +474,7 @@ export const append: { * await Effect.runPromise(program) // => [1, 2, 3, 4] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -496,6 +509,7 @@ export const prepend: { * await Effect.runPromise(program) // => [5, 6] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -524,6 +538,7 @@ export const size = (self: TxChunk): Effect.Effect => * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -552,6 +567,7 @@ export const isEmpty = (self: TxChunk): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -584,6 +600,7 @@ export const isNonEmpty = (self: TxChunk): Effect.Effect => * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -621,6 +638,7 @@ export const take: { * await Effect.runPromise(program) // => [3, 4, 5] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -658,6 +676,7 @@ export const drop: { * await Effect.runPromise(program) // => [3, 4, 5] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -697,6 +716,7 @@ export const slice: { * await Effect.runPromise(program) // => [2, 4, 6, 8] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -734,6 +754,7 @@ export const map: { * await Effect.runPromise(program) // => [2, 4, 6] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -775,6 +796,7 @@ export const filter: { * await Effect.runPromise(program) // => [1, 2, 3, 4, 5, 6] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -814,6 +836,7 @@ export const appendAll: { * await Effect.runPromise(program) // => [1, 2, 3, 4, 5, 6] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -856,6 +879,7 @@ export const prependAll: { * await Effect.runPromise(program) // => [[1, 2, 3, 4, 5, 6], [4, 5, 6]] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxDeferred.ts b/packages/effect/src/TxDeferred.ts similarity index 98% rename from repos/effect/packages/effect/src/TxDeferred.ts rename to packages/effect/src/TxDeferred.ts index 681701f0a..f9530744f 100644 --- a/repos/effect/packages/effect/src/TxDeferred.ts +++ b/packages/effect/src/TxDeferred.ts @@ -7,6 +7,7 @@ * and when another transaction completes the deferred the waiting transaction * can resume with either the success value or the typed failure. * + * @stability stable * @since 4.0.0 */ @@ -57,6 +58,7 @@ const TypeId = "~effect/TxDeferred" * await Effect.runPromise(program) // => [true, false, 42] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -106,6 +108,7 @@ const makeTxDeferred = (ref: TxRef.TxRef>>): TxDeferre * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -156,6 +159,7 @@ export { * * @see {@link poll} for inspecting the current completion state without retrying the transaction * + * @stability stable * @category getters * @since 4.0.0 */ @@ -187,6 +191,7 @@ export { * await Effect.runPromise(program) // => [Option.none(), Option.some(Result.succeed(42))] * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -215,6 +220,7 @@ export const poll = (self: TxDeferred): Effect.Effect [true, false] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -255,6 +261,7 @@ export const done: { * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -290,6 +297,7 @@ export const succeed: { * await Effect.runPromise(program) // => [true, false, Exit.fail("boom"), Option.some(Cause.fail("boom"))] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -321,6 +329,7 @@ export const fail: { * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability stable * @category guards * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxHashMap.ts b/packages/effect/src/TxHashMap.ts similarity index 98% rename from repos/effect/packages/effect/src/TxHashMap.ts rename to packages/effect/src/TxHashMap.ts index 11352eab3..9fbb4d5ed 100644 --- a/repos/effect/packages/effect/src/TxHashMap.ts +++ b/packages/effect/src/TxHashMap.ts @@ -7,6 +7,7 @@ * shared registries, counters, indexes, and other maps that need safe * read-modify-write sequences alongside related transactional state. * + * @stability stable * @since 2.0.0 */ @@ -79,6 +80,7 @@ const TxHashMapProto = { * await Effect.runPromise(program) // => 2 * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -123,6 +125,7 @@ export interface TxHashMap extends Inspectable, Pipeable { * await Effect.runPromise(program) // => Option.some({ stock: 3, price: 999 }) * ``` * + * @stability stable * @since 4.0.0 */ export declare namespace TxHashMap { @@ -252,6 +255,7 @@ export declare namespace TxHashMap { * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -288,6 +292,7 @@ export const empty = (): Effect.Effect> => * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -331,17 +336,19 @@ export const make = ( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ export const fromIterable = ( entries: Iterable -): Effect.Effect> => - Effect.gen(function*() { - const hashMap = HashMap.fromIterable(entries) +): Effect.Effect> => { + const hashMap = HashMap.fromIterable(entries) + return Effect.gen(function*() { const ref = yield* TxRef.make(hashMap) return Object.assign(Object.create(TxHashMapProto), { ref }) }) +} /** * Looks up the value for the specified key in the TxHashMap. @@ -368,6 +375,7 @@ export const fromIterable = ( * await Effect.runPromise(program) // => Option.some({ name: "Bob", role: "user" }) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -418,6 +426,7 @@ export const get: { * await Effect.runPromise(program) // => Option.some(8) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -456,6 +465,7 @@ export const set: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -505,6 +515,7 @@ export const has: { * await Effect.runPromise(program) // => 2 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -558,6 +569,7 @@ export const remove: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -592,6 +604,7 @@ export const clear = (self: TxHashMap): Effect.Effect => TxRef * await Effect.runPromise(program) // => 3 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -626,6 +639,7 @@ export const size = (self: TxHashMap): Effect.Effect => * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -656,6 +670,7 @@ export const isEmpty = (self: TxHashMap): Effect.Effect => * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -707,6 +722,7 @@ export const isNonEmpty = (self: TxHashMap): Effect.Effect * await Effect.runPromise(program) // => Option.some(500) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -776,6 +792,7 @@ export const modify: { * await Effect.runPromise(program) // => Option.some("content1.bak") * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -841,6 +858,7 @@ export const modifyAt: { * await Effect.runPromise(program) // => ["alice: admin", "bob: user", "charlie: moderator"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -880,6 +898,7 @@ export const keys = (self: TxHashMap): Effect.Effect> => * await Effect.runPromise(program) // => 95 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -910,6 +929,7 @@ export const values = (self: TxHashMap): Effect.Effect> => * await Effect.runPromise(program) // => [["host", "localhost"], ["port", "3000"], ["ssl", "false"]] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -954,6 +974,7 @@ export const entries = ( * await Effect.runPromise(program) // => Option.some(45.2) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1003,6 +1024,7 @@ export const snapshot = ( * await Effect.runPromise(program) // => 5 * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1065,6 +1087,7 @@ export const union: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1073,8 +1096,10 @@ export const removeMany: { (self: TxHashMap, keys: Iterable): Effect.Effect } = dual( 2, - (self: TxHashMap, keys: Iterable): Effect.Effect => - TxRef.update(self.ref, (map) => HashMap.removeMany(map, keys)) + (self: TxHashMap, keys: Iterable): Effect.Effect => { + const snapshot = Array.from(keys) + return TxRef.update(self.ref, (map) => HashMap.removeMany(map, snapshot)) + } ) /** @@ -1136,6 +1161,7 @@ export const removeMany: { * await Effect.runPromise(program) // => Option.some({ price: 399, stock: 3 }) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1152,7 +1178,10 @@ export const setMany: { ( self: TxHashMap, entries: Iterable - ): Effect.Effect => TxRef.update(self.ref, (map) => HashMap.setMany(map, entries)) + ): Effect.Effect => { + const snapshot = Array.from(entries) + return TxRef.update(self.ref, (map) => HashMap.setMany(map, snapshot)) + } ) /** @@ -1187,6 +1216,7 @@ export const setMany: { * await Effect.runPromise(program) // => Exit.succeed("Valid TxHashMap") * ``` * + * @stability stable * @category guards * @since 4.0.0 */ @@ -1234,6 +1264,7 @@ export const isTxHashMap = (value: unknown): value is TxHashMap => { * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1305,6 +1336,7 @@ export const getHash: { * await Effect.runPromise(program) // => ["Role admin: true", "Role user: true", "Role moderator: false"] * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1367,6 +1399,7 @@ export const hasHash: { * await Effect.runPromise(program) // => Option.some({ name: "Alice", age: 30, active: true }) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1439,6 +1472,7 @@ export const map: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1513,6 +1547,7 @@ export const filter: { * await Effect.runPromise(program) // => { quarters: 4, total: 80000, max: 25000 } * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1591,6 +1626,7 @@ export const reduce: { * await Effect.runPromise(program) // => 3 * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1653,6 +1689,7 @@ export const filterMap: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1706,6 +1743,7 @@ export const hasBy: { * await Effect.runPromise(program) // => Option.some(["task1", { priority: 1, assignee: "alice", completed: false }]) * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -1763,6 +1801,7 @@ export const findFirst: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1819,6 +1858,7 @@ export const some: { * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -1868,6 +1908,7 @@ export const every: { * result // => ["access.log: 2048 bytes (info)", "debug.log: 512 bytes (debug)", "error.log: 1024 bytes (error)"] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -1939,6 +1980,7 @@ export const forEach: { * await Effect.runPromise(program) // => 4 * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2013,6 +2055,7 @@ export const flatMap: { * await Effect.runPromise(program) // => [["alice", 30], ["charlie", 25], ["eve", 28]] * ``` * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -2053,6 +2096,7 @@ export const compact = ( * await Effect.runPromise(program) // => { language: "en-US", theme: "dark", timezone: "UTC" } * ``` * + * @stability stable * @category getters * @since 4.0.0 */ @@ -2094,6 +2138,7 @@ export const toEntries = ( * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category getters * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxHashSet.ts b/packages/effect/src/TxHashSet.ts similarity index 97% rename from repos/effect/packages/effect/src/TxHashSet.ts rename to packages/effect/src/TxHashSet.ts index d95aa7a23..b6e31a439 100644 --- a/repos/effect/packages/effect/src/TxHashSet.ts +++ b/packages/effect/src/TxHashSet.ts @@ -10,6 +10,7 @@ * membership checks, set algebra, mapping, filtering, reducing, and conversion * back to `HashSet`. * + * @stability stable * @since 2.0.0 */ @@ -83,6 +84,7 @@ const TxHashSetProto = { * await Effect.runPromise(program) * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -117,6 +119,7 @@ export interface TxHashSet extends Inspectable, Pipeable { * await Effect.runPromise(program) * ``` * + * @stability stable * @since 4.0.0 */ export declare namespace TxHashSet { @@ -175,6 +178,7 @@ const makeTxHashSet = (ref: TxRef.TxRef>): TxHashSet => * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -206,6 +210,7 @@ export const empty = (): Effect.Effect> => * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -240,15 +245,17 @@ export const make = >( * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ -export const fromIterable = (values: Iterable): Effect.Effect> => - Effect.gen(function*() { - const hashSet = HashSet.fromIterable(values) +export const fromIterable = (values: Iterable): Effect.Effect> => { + const hashSet = HashSet.fromIterable(values) + return Effect.gen(function*() { const ref = yield* TxRef.make(hashSet) return makeTxHashSet(ref) }) +} /** * Creates a TxHashSet from an existing HashSet. @@ -274,6 +281,7 @@ export const fromIterable = (values: Iterable): Effect.Effect * await Effect.runPromise(program) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -305,6 +313,7 @@ export const fromHashSet = (hashSet: HashSet.HashSet): Effect.Effect => hasProperty( * await Effect.runPromise(program) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -374,6 +384,7 @@ export const add: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -427,6 +438,7 @@ export const remove: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -464,6 +476,7 @@ export const has: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -492,6 +505,7 @@ export const size = (self: TxHashSet): Effect.Effect => * await Effect.runPromise(program) * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -521,6 +535,7 @@ export const isEmpty = (self: TxHashSet): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -551,6 +566,7 @@ export const isNonEmpty = (self: TxHashSet): Effect.Effect => * await Effect.runPromise(program) * ``` * + * @stability stable * @category mutations * @since 4.0.0 */ @@ -576,6 +592,7 @@ export const clear = (self: TxHashSet): Effect.Effect => TxRef.set(s * await Effect.runPromise(program) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -618,6 +635,7 @@ export const union: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -660,6 +678,7 @@ export const intersection: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -704,6 +723,7 @@ export const difference: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -741,6 +761,7 @@ export const isSubset: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -777,6 +798,7 @@ export const some: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -816,6 +838,7 @@ export const every: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -851,6 +874,7 @@ export const map: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category filtering * @since 4.0.0 */ @@ -908,6 +932,7 @@ export const filter: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category folding * @since 2.0.0 */ @@ -961,6 +986,7 @@ export const reduce: { * await Effect.runPromise(program) * ``` * + * @stability stable * @category converting * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/TxPriorityQueue.ts b/packages/effect/src/TxPriorityQueue.ts similarity index 96% rename from repos/effect/packages/effect/src/TxPriorityQueue.ts rename to packages/effect/src/TxPriorityQueue.ts index 2e42fdd52..2a865d796 100644 --- a/repos/effect/packages/effect/src/TxPriorityQueue.ts +++ b/packages/effect/src/TxPriorityQueue.ts @@ -9,6 +9,7 @@ * is empty, so they can be combined with other transactional reads and writes in * one atomic workflow. * + * @stability stable * @since 4.0.0 */ @@ -53,6 +54,7 @@ const TypeId = "~effect/TxPriorityQueue" * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -136,6 +138,7 @@ const mergeSorted = (chunk: Chunk, values: ReadonlyArray, ord: Order * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -158,6 +161,7 @@ export const empty = (order: Order): Effect.Effect> => * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -198,6 +202,7 @@ export function fromIterable( * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -220,6 +225,7 @@ export const make = (order: Order) => (...elements: Array): Effect.Effe * await Effect.runPromise(program) // => 3 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -241,6 +247,7 @@ export const size = (self: TxPriorityQueue): Effect.Effect => Effe * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -262,6 +269,7 @@ export const isEmpty = (self: TxPriorityQueue): Effect.Effect => * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -288,6 +296,7 @@ export const isNonEmpty = (self: TxPriorityQueue): Effect.Effect * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -322,6 +331,7 @@ export const peek = (self: TxPriorityQueue): Effect.Effect => * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -346,6 +356,7 @@ export const peekOption = (self: TxPriorityQueue): Effect.Effect * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -375,6 +386,7 @@ export const offer: { * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -383,8 +395,10 @@ export const offerAll: { (self: TxPriorityQueue, values: Iterable): Effect.Effect } = dual( 2, - (self: TxPriorityQueue, values: Iterable): Effect.Effect => - TxRef.update(self.ref, (chunk) => mergeSorted(chunk, Array.from(values).sort(self.ord), self.ord)) + (self: TxPriorityQueue, values: Iterable): Effect.Effect => { + const sorted = Array.from(values).sort(self.ord) + return TxRef.update(self.ref, (chunk) => mergeSorted(chunk, sorted, self.ord)) + } ) /** @@ -403,6 +417,7 @@ export const offerAll: { * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -433,6 +448,7 @@ export const take = (self: TxPriorityQueue): Effect.Effect => * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -458,6 +474,7 @@ export const takeAll = (self: TxPriorityQueue): Effect.Effect> => * await Effect.runPromise(program) // => Option.none() * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -486,6 +503,7 @@ export const takeOption = (self: TxPriorityQueue): Effect.Effect * await Effect.runPromise(program) // => [1, 2] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -522,6 +540,7 @@ export const takeUpTo: { * await Effect.runPromise(program) // => [1, 3, 5] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -551,6 +570,7 @@ export const removeIf: { * await Effect.runPromise(program) // => [2, 4] * ``` * + * @stability stable * @category filtering * @since 2.0.0 */ @@ -579,6 +599,7 @@ export const retainIf: { * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category converting * @since 2.0.0 */ @@ -601,6 +622,7 @@ export const toArray = (self: TxPriorityQueue): Effect.Effect> => * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability stable * @category guards * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxPubSub.ts b/packages/effect/src/TxPubSub.ts similarity index 98% rename from repos/effect/packages/effect/src/TxPubSub.ts rename to packages/effect/src/TxPubSub.ts index d31ee9cf9..2cfaac03d 100644 --- a/repos/effect/packages/effect/src/TxPubSub.ts +++ b/packages/effect/src/TxPubSub.ts @@ -7,6 +7,7 @@ * bounded, dropping, sliding, and unbounded hubs, publishing helpers, scoped * subscriptions, shutdown operations, and a guard. * + * @stability stable * @since 4.0.0 */ import * as Arr from "./Array.ts" @@ -47,6 +48,7 @@ const TypeId = "~effect/TxPubSub" * await Effect.runPromise(program) // => "hello" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -122,6 +124,7 @@ const makeTxPubSub = ( * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -160,6 +163,7 @@ export const bounded = (capacity: number): Effect.Effect> * await Effect.runPromise(program) // => [1, 2] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -196,6 +200,7 @@ export const dropping = (capacity: number): Effect.Effect * await Effect.runPromise(program) // => 2 * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -229,6 +234,7 @@ export const sliding = (capacity: number): Effect.Effect> * await Effect.runPromise(program) // => "msg" * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -259,6 +265,7 @@ export const unbounded = (): Effect.Effect> => * await Effect.runPromise(program) // => 16 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -288,6 +295,7 @@ export const capacity = (self: TxPubSub): number => self.capacity * await Effect.runPromise(program) // => 2 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -318,6 +326,7 @@ export const size = (self: TxPubSub): Effect.Effect => * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -347,6 +356,7 @@ export const isEmpty = (self: TxPubSub): Effect.Effect => Effect. * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 4.0.0 */ @@ -368,6 +378,7 @@ export const isNonEmpty = (self: TxPubSub): Effect.Effect => Effe * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -399,6 +410,7 @@ export const isFull = (self: TxPubSub): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability stable * @category predicates * @since 2.0.0 */ @@ -439,6 +451,7 @@ export const isShutdown = (self: TxPubSub): Effect.Effect => TxRe * await Effect.runPromise(program) // => [true, "hello"] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -493,6 +506,7 @@ export const publish: { * await Effect.runPromise(program) // => [1, 2, 3] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -548,6 +562,7 @@ export const publishAll: { * await Effect.runPromise(program) // => ["broadcast", "broadcast"] * ``` * + * @stability unstable * @category mutations * @since 2.0.0 */ @@ -572,6 +587,7 @@ export const subscribe = (self: TxPubSub): Effect.Effect( * @see {@link acquireSubscriber} for the matching transactional acquire step * @see {@link subscribe} for the scoped acquire and release wrapper * + * @stability unstable * @category mutations * @since 4.0.0 */ @@ -667,6 +684,7 @@ const makeSubscriberQueue = ( * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -702,6 +720,7 @@ export const shutdown = (self: TxPubSub): Effect.Effect => * await Effect.runPromise(program) // => true * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -728,6 +747,7 @@ export const awaitShutdown = (self: TxPubSub): Effect.Effect => * TxPubSub.isTxPubSub(someValue) // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxQueue.ts b/packages/effect/src/TxQueue.ts similarity index 98% rename from repos/effect/packages/effect/src/TxQueue.ts rename to packages/effect/src/TxQueue.ts index 1348abefe..8c9d80908 100644 --- a/repos/effect/packages/effect/src/TxQueue.ts +++ b/packages/effect/src/TxQueue.ts @@ -8,6 +8,7 @@ * full bounded queue. This makes the queue useful for coordinating producers * and consumers alongside other transactional state changes. * + * @stability stable * @since 4.0.0 */ import type * as Arr from "./Array.ts" @@ -44,6 +45,7 @@ import type * as Types from "./Types.ts" * state._tag // => "Open" * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -67,6 +69,7 @@ const TypeId = "~effect/TxQueue" /** * Namespace containing type definitions for TxEnqueue variance annotations. * + * @stability stable * @since 4.0.0 */ export declare namespace TxEnqueue { @@ -85,6 +88,7 @@ export declare namespace TxEnqueue { /** * Namespace containing type definitions for TxDequeue variance annotations. * + * @stability stable * @since 4.0.0 */ export declare namespace TxDequeue { @@ -103,6 +107,7 @@ export declare namespace TxDequeue { /** * Namespace containing type definitions for TxQueue variance annotations. * + * @stability stable * @since 4.0.0 */ export declare namespace TxQueue { @@ -123,6 +128,7 @@ export declare namespace TxQueue { * This interface contains the core properties needed for queue state inspection * operations like size, capacity, and completion status. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -167,6 +173,7 @@ export interface TxQueueState extends Inspectable { * await Effect.runPromise(program) // => true * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -202,6 +209,7 @@ export interface TxEnqueue extends TxQueueState { * await Effect.runPromise(program) // => [42, "processing failed", "processing failed"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -238,6 +246,7 @@ export interface TxDequeue extends TxQueueState { * await Effect.runPromise(program) // => [true, 42, "queue failed"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -257,6 +266,7 @@ export interface TxQueue extends TxEnqueue, Tx * TxQueue.isTxEnqueue(someValue) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -274,6 +284,7 @@ export const isTxEnqueue = (u: unknown): u is TxEnqueu * TxQueue.isTxDequeue(someValue) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -291,6 +302,7 @@ export const isTxDequeue = (u: unknown): u is TxDequeu * TxQueue.isTxQueue(someValue) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -352,6 +364,7 @@ const TxQueueProto = { * await Effect.runPromise(program) // => 1 * ``` * + * @stability unstable * @category constructors * @since 2.0.0 */ @@ -399,6 +412,7 @@ export const bounded = ( * await Effect.runPromise(program) // => 2 * ``` * + * @stability unstable * @category constructors * @since 2.0.0 */ @@ -442,6 +456,7 @@ export const unbounded = (): Effect.Effect> * await Effect.runPromise(program) // => false * ``` * + * @stability unstable * @category constructors * @since 2.0.0 */ @@ -489,6 +504,7 @@ export const dropping = ( * await Effect.runPromise(program) // => 2 * ``` * + * @stability unstable * @category constructors * @since 2.0.0 */ @@ -533,6 +549,7 @@ export const sliding = ( * await Effect.runPromise(program) // => true * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -601,6 +618,7 @@ export const offer: { * await Effect.runPromise(program) // => [] * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -656,6 +674,7 @@ export const offerAll: { * await Effect.runPromise(program) // => [42, Exit.fail("queue error")] * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -712,6 +731,7 @@ export const take = (self: TxDequeue): Effect.Effect => * await Effect.runPromise(program) // => [Option.none(), Option.some(42)] * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -771,6 +791,7 @@ export const poll = (self: TxDequeue): Effect.Effect Exit.fail("processing error") * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -829,6 +850,7 @@ export const takeAll = (self: TxDequeue): Effect.Effect [[1, 2, 3, 4], [5, 6, 7, 8, 9]] * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -924,6 +946,7 @@ export const takeN: { * await Effect.runPromise(program) // => [[1, 2, 3, 4, 5], [6, 7, 8]] * ``` * + * @stability unstable * @category taking * @since 2.0.0 */ @@ -1023,6 +1046,7 @@ export const takeBetween: { * await Effect.runPromise(errorExample) // => Exit.fail("queue failed") * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -1060,6 +1084,7 @@ export const peek = (self: TxDequeue): Effect.Effect => * await Effect.runPromise(program) // => 3 * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -1086,6 +1111,7 @@ export const size = (self: TxQueueState): Effect.Effect => TxChunk.size( * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability unstable * @category predicates * @since 2.0.0 */ @@ -1112,6 +1138,7 @@ export const isEmpty = (self: TxQueueState): Effect.Effect => TxChunk.i * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -1138,6 +1165,7 @@ export const isNonEmpty = (self: TxQueueState): Effect.Effect => TxChun * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 2.0.0 */ @@ -1169,6 +1197,7 @@ export const isFull = (self: TxQueueState): Effect.Effect => * await Effect.runPromise(program) // => true * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1197,6 +1226,7 @@ export const interrupt = (self: TxEnqueue): Effect.Effect = * await Effect.runPromise(program) // => true * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1245,6 +1275,7 @@ export const fail: { * await Effect.runPromise(program) // => [Cause.interrupt(), true] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1301,6 +1332,7 @@ export const failCause: { * await Effect.runPromise(program) // => [true, Exit.fail(Cause.Done()), Exit.fail(Cause.Done())] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1334,6 +1366,7 @@ export const end = (self: TxEnqueue): Effect.Effect [5, [1, 2, 3, 4, 5], 0] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1384,6 +1417,7 @@ export const clear = (self: TxEnqueue): Effect.Effect, Excl * await Effect.runPromise(program) // => [5, 0, true] * ``` * + * @stability unstable * @category combinators * @since 2.0.0 */ @@ -1414,6 +1448,7 @@ export const shutdown = (self: TxEnqueue): Effect.Effect => * await Effect.runPromise(program) // => [true, false] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -1442,6 +1477,7 @@ export const isOpen = (self: TxQueueState): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -1469,6 +1505,7 @@ export const isClosing = (self: TxQueueState): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -1496,6 +1533,7 @@ export const isDone = (self: TxQueueState): Effect.Effect => * await Effect.runPromise(program) // => [false, true] * ``` * + * @stability unstable * @category predicates * @since 2.0.0 */ @@ -1522,6 +1560,7 @@ export const isShutdown = (self: TxQueueState): Effect.Effect => isDone * await Effect.runPromise(program) // => "Queue completed successfully" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxReentrantLock.ts b/packages/effect/src/TxReentrantLock.ts similarity index 93% rename from repos/effect/packages/effect/src/TxReentrantLock.ts rename to packages/effect/src/TxReentrantLock.ts index cfe2c0117..ad4540862 100644 --- a/repos/effect/packages/effect/src/TxReentrantLock.ts +++ b/packages/effect/src/TxReentrantLock.ts @@ -8,9 +8,11 @@ * until the lock becomes available. This module includes manual, scoped, and * wrapper-style operations for read and write locking. * + * @stability stable * @since 4.0.0 */ import * as Effect from "./Effect.ts" +import { dual } from "./Function.ts" import * as HashMap from "./HashMap.ts" import type { Inspectable } from "./Inspectable.ts" import { NodeInspectSymbol, toJson } from "./Inspectable.ts" @@ -61,6 +63,7 @@ const emptyState: LockState = { * await Effect.runPromise(program) // => ["reading", "writing"] * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -105,6 +108,7 @@ const TxReentrantLockProto: Omit = * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -141,6 +145,7 @@ export const make = (): Effect.Effect => * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -197,6 +202,7 @@ export const acquireRead = (self: TxReentrantLock): Effect.Effect => * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -293,6 +299,7 @@ const releaseReadFor = (self: TxReentrantLock, fiberId: number): Effect.Effect => * await Effect.runPromise(program) // => [1, 0] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -392,7 +401,8 @@ export const readLock = (self: TxReentrantLock): Effect.Effect Effect.acquireRelease( acquireRead(self), - () => releaseReadFor(self, fiber.id) + () => releaseReadFor(self, fiber.id), + { interruptible: true } ) ) @@ -422,6 +432,7 @@ export const readLock = (self: TxReentrantLock): Effect.Effect [1, 0] * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -429,10 +440,25 @@ export const writeLock = (self: TxReentrantLock): Effect.Effect Effect.acquireRelease( acquireWrite(self), - () => releaseWriteFor(self, fiber.id) + () => releaseWriteFor(self, fiber.id), + { interruptible: true } ) ) +/** + * Runs `effect` between `acquire` and `release`. Acquisition and `effect` + * inherit the caller's interruptibility, while the handoff from a successful + * acquisition to the release finalizer stays protected. + */ +const withAcquired = ( + acquire: Effect.Effect, + effect: Effect.Effect, + release: Effect.Effect +): Effect.Effect => + Effect.uninterruptibleMask((restore) => + Effect.flatMap(restore(acquire), () => Effect.ensuring(restore(effect), release)) + ) + /** * Runs the provided effect while holding a read lock. The lock is automatically * released after the effect completes, fails, or is interrupted. @@ -453,29 +479,18 @@ export const writeLock = (self: TxReentrantLock): Effect.Effect "read data" * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ export const withReadLock: { (effect: Effect.Effect): (self: TxReentrantLock) => Effect.Effect (self: TxReentrantLock, effect: Effect.Effect): Effect.Effect -} = ((...args: Array) => { - if (args.length === 1) { - const [effect] = args - return (self: TxReentrantLock) => - Effect.acquireUseRelease( - acquireRead(self), - () => effect, - () => releaseRead(self) - ) - } - const [self, effect] = args - return Effect.acquireUseRelease( - acquireRead(self), - () => effect, - () => releaseRead(self) - ) -}) as any +} = dual( + 2, + (self: TxReentrantLock, effect: Effect.Effect): Effect.Effect => + withAcquired(acquireRead(self), effect, releaseRead(self)) +) /** * Runs the provided effect while holding a write lock. The lock is automatically @@ -497,29 +512,18 @@ export const withReadLock: { * await Effect.runPromise(program) // => "wrote data" * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ export const withWriteLock: { (effect: Effect.Effect): (self: TxReentrantLock) => Effect.Effect (self: TxReentrantLock, effect: Effect.Effect): Effect.Effect -} = ((...args: Array) => { - if (args.length === 1) { - const [effect] = args - return (self: TxReentrantLock) => - Effect.acquireUseRelease( - acquireWrite(self), - () => effect, - () => releaseWrite(self) - ) - } - const [self, effect] = args - return Effect.acquireUseRelease( - acquireWrite(self), - () => effect, - () => releaseWrite(self) - ) -}) as any +} = dual( + 2, + (self: TxReentrantLock, effect: Effect.Effect): Effect.Effect => + withAcquired(acquireWrite(self), effect, releaseWrite(self)) +) /** * Runs an effect while holding a write lock. @@ -545,6 +549,7 @@ export const withWriteLock: { * await Effect.runPromise(program) // => "exclusive operation" * ``` * + * @stability stable * @category mutations * @since 2.0.0 */ @@ -576,6 +581,7 @@ export const withLock: { * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -605,6 +611,7 @@ export const readLocks = (self: TxReentrantLock): Effect.Effect => * await Effect.runPromise(program) // => 0 * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -630,6 +637,7 @@ export const writeLocks = (self: TxReentrantLock): Effect.Effect => * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -655,6 +663,7 @@ export const locked = (self: TxReentrantLock): Effect.Effect => * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -680,6 +689,7 @@ export const readLocked = (self: TxReentrantLock): Effect.Effect => * await Effect.runPromise(program) // => false * ``` * + * @stability stable * @category getters * @since 2.0.0 */ @@ -706,6 +716,7 @@ export const writeLocked = (self: TxReentrantLock): Effect.Effect => * TxReentrantLock.isTxReentrantLock(someValue) // => false * ``` * + * @stability stable * @category guards * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/TxRef.ts b/packages/effect/src/TxRef.ts similarity index 97% rename from repos/effect/packages/effect/src/TxRef.ts rename to packages/effect/src/TxRef.ts index d5a86f658..7f9b4c44e 100644 --- a/repos/effect/packages/effect/src/TxRef.ts +++ b/packages/effect/src/TxRef.ts @@ -9,6 +9,7 @@ * core operations for reading, setting, updating, and modifying a transactional * value while returning a separate result. * + * @stability stable * @since 4.0.0 */ import * as Effect from "./Effect.ts" @@ -55,6 +56,7 @@ const TypeId = "~effect/TxRef" * await Effect.runPromise(program) // => 1 * ``` * + * @stability stable * @category models * @since 4.0.0 */ @@ -95,6 +97,7 @@ export interface TxRef extends Pipeable { * await Effect.runPromise(program) // => [42, "Bob"] * ``` * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -122,6 +125,7 @@ export const make = (initial: A) => Effect.sync(() => makeUnsafe(initial)) * config.value // => { timeout: 5000, retries: 3 } * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -169,6 +173,7 @@ const journalEntry = (state: Effect.Transaction["Service"], self: TxRef) = * await Effect.runPromise(program) // => [0, 1] * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -218,6 +223,7 @@ export const modify: { * await Effect.runPromise(program) // => 20 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -255,6 +261,7 @@ export const update: { * await Effect.runPromise(program) // => 42 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -290,6 +297,7 @@ export const get = (self: TxRef): Effect.Effect => * await Effect.runPromise(program) // => 100 * ``` * + * @stability stable * @category combinators * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/TxSemaphore.ts b/packages/effect/src/TxSemaphore.ts similarity index 94% rename from repos/effect/packages/effect/src/TxSemaphore.ts rename to packages/effect/src/TxSemaphore.ts index e42fd2631..83f648adb 100644 --- a/repos/effect/packages/effect/src/TxSemaphore.ts +++ b/packages/effect/src/TxSemaphore.ts @@ -7,6 +7,7 @@ * creating semaphores, checking capacity and availability, acquiring or * releasing permits, and running effects while permits are held. * + * @stability stable * @since 4.0.0 */ @@ -61,6 +62,7 @@ const TypeId = "~effect/TxSemaphore" * @see {@link withPermit} for automatically acquiring and releasing one permit * @see {@link acquire} for manually acquiring one permit transactionally * + * @stability stable * @category models * @since 4.0.0 */ @@ -122,6 +124,7 @@ const makeTxSemaphore = (permitsRef: TxRef.TxRef, capacity: number): TxS * @see {@link available} for reading the current available permit count * @see {@link capacity} for reading the fixed total permit count * + * @stability stable * @category constructors * @since 2.0.0 */ @@ -167,6 +170,7 @@ export const make = (permits: number): Effect.Effect => * * @see {@link capacity} for reading the fixed total permit count * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -200,6 +204,7 @@ export const available = (self: TxSemaphore): Effect.Effect => TxRef.get * * @see {@link available} for reading the current available permit count * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -236,6 +241,7 @@ export const capacity = (self: TxSemaphore): Effect.Effect => Effect.suc * @see {@link release} for returning one permit * @see {@link withPermit} for automatic acquire and release around an effect * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -286,6 +292,7 @@ export const acquire = (self: TxSemaphore): Effect.Effect => * @see {@link releaseN} for returning multiple permits * @see {@link withPermits} for automatic acquire and release around an effect * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -336,6 +343,7 @@ export const acquireN: { * @see {@link acquire} for waiting until one permit is available * @see {@link tryAcquireN} for attempting to acquire multiple permits without blocking * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -379,6 +387,7 @@ export const tryAcquire = (self: TxSemaphore): Effect.Effect => * @see {@link acquireN} for waiting until all requested permits are available * @see {@link tryAcquire} for attempting to acquire one permit without blocking * + * @stability stable * @category combinators * @since 4.0.0 */ @@ -434,6 +443,7 @@ export const tryAcquireN: { * @see {@link acquire} for manually acquiring one permit * @see {@link releaseN} for returning multiple permits * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -479,6 +489,7 @@ export const release = (self: TxSemaphore): Effect.Effect => * @see {@link acquireN} for manually acquiring multiple permits * @see {@link release} for returning one permit * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -495,6 +506,20 @@ export const releaseN: { }) }) +/** + * Runs `effect` between `acquire` and `release`. Acquisition and `effect` + * inherit the caller's interruptibility, while the handoff from a successful + * acquisition to the release finalizer stays protected. + */ +const withAcquired = ( + acquire: Effect.Effect, + effect: Effect.Effect, + release: Effect.Effect +): Effect.Effect => + Effect.uninterruptibleMask((restore) => + Effect.flatMap(restore(acquire), () => Effect.ensuring(restore(effect), release)) + ) + /** * Executes an effect with a single permit from the semaphore. The permit is * automatically acquired before execution and released afterwards, even if the @@ -542,6 +567,7 @@ export const releaseN: { * @see {@link withPermitScoped} for acquiring one permit for the current scope * @see {@link acquire} for manual single-permit acquisition * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -549,21 +575,9 @@ export const withPermit: { (self: TxSemaphore): (effect: Effect.Effect) => Effect.Effect (self: TxSemaphore, effect: Effect.Effect): Effect.Effect } = ((...args: Array) => { - if (args.length === 1) { - const [self] = args - return (effect: Effect.Effect) => - Effect.acquireUseRelease( - acquire(self), - () => effect, - () => release(self) - ) - } - const [self, effect] = args - return Effect.acquireUseRelease( - acquire(self), - () => effect, - () => release(self) - ) + const [self] = args + const run = (effect: Effect.Effect) => withAcquired(acquire(self), effect, release(self)) + return args.length === 1 ? run : run(args[1]) }) as any /** @@ -616,6 +630,7 @@ export const withPermit: { * @see {@link withPermit} for automatically acquiring and releasing one permit * @see {@link acquireN} for manual multi-permit acquisition * + * @stability stable * @category combinators * @since 2.0.0 */ @@ -623,21 +638,9 @@ export const withPermits: { (self: TxSemaphore, n: number): (effect: Effect.Effect) => Effect.Effect (self: TxSemaphore, n: number, effect: Effect.Effect): Effect.Effect } = ((...args: Array) => { - if (args.length === 2) { - const [self, n] = args - return (effect: Effect.Effect) => - Effect.acquireUseRelease( - acquireN(self, n), - () => effect, - () => releaseN(self, n) - ) - } - const [self, n, effect] = args - return Effect.acquireUseRelease( - acquireN(self, n), - () => effect, - () => releaseN(self, n) - ) + const [self, n] = args + const run = (effect: Effect.Effect) => withAcquired(acquireN(self, n), effect, releaseN(self, n)) + return args.length === 2 ? run : run(args[2]) }) as any /** @@ -688,13 +691,15 @@ export const withPermits: { * @see {@link withPermit} for acquiring one permit around a single effect * @see {@link acquire} for manual single-permit acquisition * + * @stability stable * @category combinators * @since 2.0.0 */ export const withPermitScoped = (self: TxSemaphore): Effect.Effect => Effect.acquireRelease( acquire(self), - () => release(self) + () => release(self), + { interruptible: true } ) /** @@ -729,6 +734,7 @@ export const withPermitScoped = (self: TxSemaphore): Effect.Effect, typeof TypeId | "ref" * * @see {@link changes} for subscribing to the created reference * + * @stability stable * @category constructors * @since 3.10.0 */ @@ -155,6 +158,7 @@ export const make = (value: A): Effect.Effect> => * * @see {@link changes} for reading the current value and subsequent updates * + * @stability stable * @category getters * @since 3.10.0 */ @@ -190,6 +194,7 @@ export const get = (self: TxSubscriptionRef): Effect.Effect => TxRef.ge * @see {@link update} for deriving the next value without a separate return value * @see {@link set} for replacing the value directly * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -241,6 +246,7 @@ export const modify: { * @see {@link update} for deriving the new value from the current value * @see {@link getAndSet} for setting while returning the previous value * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -278,6 +284,7 @@ export const set: { * @see {@link set} for replacing the value directly * @see {@link updateAndGet} for returning the new value after the update * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -316,6 +323,7 @@ export const update: { * @see {@link set} for setting without returning the previous value * @see {@link getAndUpdate} for deriving the new value from the previous value * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -353,6 +361,7 @@ export const getAndSet: { * @see {@link update} for updating without returning the previous value * @see {@link updateAndGet} for returning the new value instead * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -390,6 +399,7 @@ export const getAndUpdate: { * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for returning the previous value instead * + * @stability stable * @category mutations * @since 3.10.0 */ @@ -443,6 +453,7 @@ export const updateAndGet: { * * @see {@link changesStream} for subscribing through a `Stream` * + * @stability unstable * @category subscriptions * @since 3.10.0 */ @@ -490,6 +501,7 @@ export const changes = ( * * @see {@link changes} for subscribing through a transactional queue * + * @stability stable * @category subscriptions * @since 3.10.0 */ @@ -523,6 +535,7 @@ export const changesStream = (self: TxSubscriptionRef): Stream.Stream> = `${N}` extends `- * * @see {@link TupleOfAtLeast} * + * @stability stable * @category utility types * @since 3.3.0 */ @@ -83,6 +85,7 @@ export type TupleOf = N extends N ? number extends N ? Arra * * @see {@link TupleOf} * + * @stability stable * @category utility types * @since 3.3.0 */ @@ -118,6 +121,7 @@ export type TupleOfAtLeast = [...TupleOf, ...Array * @see {@link ExtractTag} * @see {@link ExcludeTag} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -154,6 +158,7 @@ export type Tags = E extends { readonly _tag: string } ? E["_tag"] : never * @see {@link ExtractTag} * @see {@link Tags} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -189,6 +194,7 @@ export type ExcludeTag = Exclude * @see {@link ExcludeTag} * @see {@link Tags} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -223,6 +229,7 @@ export type ExtractTag = E extends { readonly _tag: infer T * * @see {@link IsUnion} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -256,6 +263,7 @@ export type UnionToIntersection = (T extends any ? (x: T) => any : never) ext * @see {@link MergeLeft} * @see {@link MergeRight} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -288,6 +296,7 @@ export type Simplify = { * * @see {@link EqualsWith} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -318,6 +327,7 @@ export type Equals = (() => T extends X ? 1 : 2) extends < * * @see {@link Equals} * + * @stability stable * @category utility types * @since 3.15.0 */ @@ -345,6 +355,7 @@ export type EqualsWith = (() => T extends A ? 1 : 2) extends ( * type No = Types.Has<{ a: number }, "b" | "c"> // false * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -381,6 +392,7 @@ export type Has = (Key extends infer K ? K extends keyof * @see {@link MergeRight} * @see {@link Simplify} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -415,6 +427,7 @@ export type MergeLeft = MergeRight * @see {@link MergeLeft} * @see {@link Simplify} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -448,6 +461,7 @@ export type MergeRight = Simplify< * const unbounded: Types.Concurrency = "unbounded" * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -489,6 +503,7 @@ export type Concurrency = number | "unbounded" * * @see {@link DeepMutable} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -537,6 +552,7 @@ type DeepMutableIsOpaque = Extract extends never * * @see {@link Mutable} * + * @stability stable * @category utility types * @since 3.1.0 */ @@ -573,6 +589,7 @@ export type DeepMutable = T extends ReadonlyMap ? Map("a", "b") * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -609,6 +626,7 @@ export type NoInfer = [A][A extends any ? 0 : never] * @see {@link Covariant} * @see {@link Contravariant} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -621,6 +639,7 @@ export type Invariant = (_: A) => A * * Use when referring to type-level helpers nested under `Invariant`. * + * @stability stable * @since 3.9.0 */ export declare namespace Invariant { @@ -681,6 +700,7 @@ export declare namespace Invariant { * @see {@link Contravariant} * @see {@link Invariant} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -693,6 +713,7 @@ export type Covariant = (_: never) => A * * Use when referring to type-level helpers nested under `Covariant`. * + * @stability stable * @since 3.9.0 */ export declare namespace Covariant { @@ -756,6 +777,7 @@ export declare namespace Covariant { * @see {@link Covariant} * @see {@link Invariant} * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -768,6 +790,7 @@ export type Contravariant = (_: A) => void * * Use when referring to type-level helpers nested under `Contravariant`. * + * @stability stable * @since 3.9.0 */ export declare namespace Contravariant { @@ -805,6 +828,7 @@ export declare namespace Contravariant { * * Use to erase an empty object type from an API result or parameter position. * + * @stability stable * @category utility types * @since 3.19.20 */ @@ -832,6 +856,7 @@ export type VoidIfEmpty = keyof S extends never ? void : S * const witness: Result = "value" * ``` * + * @stability stable * @category utility types * @since 2.0.0 */ @@ -862,6 +887,7 @@ export type NotFunction = T extends Function ? never : T * const accepted: Types.NoExcessProperties = { a: 1, b: "value" } * ``` * + * @stability stable * @category utility types * @since 3.9.0 */ @@ -882,6 +908,7 @@ export type NoExcessProperties = T & Readonly = [T] extends [UnionToIntersection] ? false : true * @see {@link ExtractReason} * @see {@link ExcludeReason} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1008,6 +1038,7 @@ export type ReasonOf = E extends { readonly reason: infer R } ? R : never * @see {@link ReasonOf} * @see {@link ExtractReason} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1046,6 +1077,7 @@ export type ReasonTags = E extends { readonly reason: { readonly _tag: string * @see {@link ReasonOf} * @see {@link ReasonTags} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1089,6 +1121,7 @@ export type ExtractReason = E extends { readonly reason: in * @see {@link ReasonOf} * @see {@link ReasonTags} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1133,6 +1166,7 @@ export type NarrowReason = E extends { readonly reason: inf * @see {@link ReasonOf} * @see {@link ReasonTags} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1173,6 +1207,7 @@ export type OmitReason = E extends { readonly reason: infer * @see {@link ReasonOf} * @see {@link ReasonTags} * + * @stability stable * @category utility types * @since 4.0.0 */ @@ -1191,6 +1226,7 @@ type WithoutIndexSignature_ = { /** * Extracts the keys of required properties from a type. * + * @stability stable * @category utility types * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/UndefinedOr.ts b/packages/effect/src/UndefinedOr.ts similarity index 97% rename from repos/effect/packages/effect/src/UndefinedOr.ts rename to packages/effect/src/UndefinedOr.ts index eb9be8845..ff7e94f97 100644 --- a/repos/effect/packages/effect/src/UndefinedOr.ts +++ b/packages/effect/src/UndefinedOr.ts @@ -8,6 +8,7 @@ * cases, throwing when a value is missing, adapting throwing functions, and * building reducers or combiners. * + * @stability stable * @since 4.0.0 */ import * as Combiner from "./Combiner.ts" @@ -25,6 +26,7 @@ import * as Reducer from "./Reducer.ts" * * @see {@link match} when you need to handle the `undefined` case explicitly * + * @stability stable * @category mapping * @since 4.0.0 */ @@ -45,6 +47,7 @@ export const map: { * @see {@link map} for transforming defined values while preserving `undefined` * @see {@link getOrThrowWith} for throwing when the value is `undefined` instead of returning a fallback branch * + * @stability stable * @category pattern matching * @since 4.0.0 */ @@ -82,6 +85,7 @@ export const match: { * @see {@link getOrThrow} for the default-error sibling * @see {@link match} for handling defined and undefined cases without throwing * + * @stability stable * @category getters * @since 4.0.0 */ @@ -112,6 +116,7 @@ export const getOrThrowWith: { * @see {@link getOrThrowWith} for the sibling that lets callers choose the thrown value * @see {@link match} for handling defined and undefined cases without throwing * + * @stability stable * @category getters * @since 4.0.0 */ @@ -133,6 +138,7 @@ export const getOrThrow: (self: A | undefined) => A = getOrThrowWith(() => * Thrown values are discarded. If the wrapped function can successfully return * `undefined`, that success is indistinguishable from a thrown failure. * + * @stability stable * @category converting * @since 4.0.0 */ @@ -165,6 +171,7 @@ export const liftThrowable = , B>( * are combined with `combiner.combine`. The reducer's initial value is * `undefined`. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -194,6 +201,7 @@ export function makeReducer(combiner: Combiner.Combiner): Reducer.Reducer< * @see {@link makeReducerFailFast} if you have a `Reducer` and want to lift it * to `UndefinedOr` values. * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -222,6 +230,7 @@ export function makeCombinerFailFast(combiner: Combiner.Combiner): Combine * @see {@link makeCombinerFailFast} if you only have a `Combiner` and want to * lift it to `UndefinedOr` values. * + * @stability stable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Unify.ts b/packages/effect/src/Unify.ts similarity index 97% rename from repos/effect/packages/effect/src/Unify.ts rename to packages/effect/src/Unify.ts index a2e280985..5f7315ebe 100644 --- a/repos/effect/packages/effect/src/Unify.ts +++ b/packages/effect/src/Unify.ts @@ -8,6 +8,7 @@ * exports the protocol symbols, the `Unify` type that performs normalization, * and `unify`, an identity function that changes only the inferred type. * + * @stability stable * @since 2.0.0 */ @@ -29,6 +30,7 @@ import { identity } from "./Function.ts" * @see {@link typeSymbol} for storing the source type information used during unification * @see {@link ignoreSymbol} for excluding protocol entries from unification * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -48,6 +50,7 @@ export declare const unifySymbol: unique symbol * behavior in Effect types. It's typically used in type-level operations * to enable automatic type unification. * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -68,6 +71,7 @@ export type unifySymbol = typeof unifySymbol * * @see {@link unifySymbol} for defining how protocol entries widen * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -87,6 +91,7 @@ export declare const typeSymbol: unique symbol * in types that support unification. It's used in type-level operations * to access and manipulate type information. * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -107,6 +112,7 @@ export type typeSymbol = typeof typeSymbol * * @see {@link unifySymbol} for defining the protocol entries being filtered * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -126,6 +132,7 @@ export declare const ignoreSymbol: unique symbol * be ignored during unification operations. It's used in type-level operations * to exclude specific types from the unification process. * + * @stability stable * @category symbols * @since 2.0.0 */ @@ -202,6 +209,7 @@ type FilterOut = A extends any ? typeSymbol extends keyof A ? never : A : nev * * @see {@link unify} for applying this normalization to a value or function * + * @stability stable * @category models * @since 2.0.0 */ @@ -266,6 +274,7 @@ export type Unify = Values< * * @see {@link Unify} for the type-level normalization applied by this helper * + * @stability stable * @category utility types * @since 2.0.0 */ diff --git a/repos/effect/packages/effect/src/Utils.ts b/packages/effect/src/Utils.ts similarity index 98% rename from repos/effect/packages/effect/src/Utils.ts rename to packages/effect/src/Utils.ts index 9c9a3c83a..d35eff6c3 100644 --- a/repos/effect/packages/effect/src/Utils.ts +++ b/packages/effect/src/Utils.ts @@ -8,6 +8,7 @@ * used by modules such as `Effect`, `Option`, and `Result` to type their * `gen` APIs. * + * @stability stable * @since 2.0.0 */ import type { Kind, TypeLambda } from "./HKT.ts" @@ -45,6 +46,7 @@ import type * as Types from "./Types.ts" * ``` * * @see {@link Gen} for the type-level signature that relies on `SingleShotGen` + * @stability stable * @category constructors * @since 2.0.0 */ @@ -113,6 +115,7 @@ export class SingleShotGen implements Iterator { * ``` * * @see {@link Gen} for the type-level signature that uses `Variance` + * @stability stable * @category models * @since 2.0.0 */ @@ -153,6 +156,7 @@ export interface Variance { * * @see {@link Variance} for encoding the variance used for inference * @see {@link SingleShotGen} for the iterator protocol that makes yielding work + * @stability stable * @category models * @since 2.0.0 */ diff --git a/packages/effect/src/Version.ts b/packages/effect/src/Version.ts new file mode 100644 index 000000000..4f5fde320 --- /dev/null +++ b/packages/effect/src/Version.ts @@ -0,0 +1,38 @@ +/** + * The `effect` package version used in telemetry headers, resources, and scopes. + * + * @stability unstable + * @since 4.0.2 + */ +import { version } from "./internal/version.ts" + +let currentVersion: string = version + +/** + * Returns the current version of the `effect` package. + * + * **Details** + * + * Defaults to the release version unless overridden by `setCurrentVersion`. + * + * @stability unstable + * @category getters + * @since 4.0.2 + */ +export const getCurrentVersion = (): string => currentVersion + +/** + * Overrides the version returned by `getCurrentVersion`. + * + * **Gotchas** + * + * Applies to this copy of the module. Existing telemetry layers retain the + * version they read when built. + * + * @stability unstable + * @category setters + * @since 4.0.2 + */ +export const setCurrentVersion = (version: string): void => { + currentVersion = version +} diff --git a/repos/effect/packages/effect/src/ai/AiError.ts b/packages/effect/src/ai/AiError.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/AiError.ts rename to packages/effect/src/ai/AiError.ts diff --git a/repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts b/packages/effect/src/ai/AnthropicStructuredOutput.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts rename to packages/effect/src/ai/AnthropicStructuredOutput.ts diff --git a/repos/effect/packages/effect/src/ai/Chat.ts b/packages/effect/src/ai/Chat.ts similarity index 99% rename from repos/effect/packages/effect/src/ai/Chat.ts rename to packages/effect/src/ai/Chat.ts index c55c69b41..f8c25ac36 100644 --- a/repos/effect/packages/effect/src/ai/Chat.ts +++ b/packages/effect/src/ai/Chat.ts @@ -509,7 +509,7 @@ const makeUnsafe = (history: Ref.Ref) => { (effect, options) => Effect.withSpan(effect, "Chat.generateObject", { attributes: { - objectName: LanguageModel.getObjectName(options.objectName, options.schema) + "effect.ai.object_name": LanguageModel.getObjectName(options.objectName, options.schema) }, captureStackTrace: false }) diff --git a/repos/effect/packages/effect/src/ai/Decision.ts b/packages/effect/src/ai/Decision.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Decision.ts rename to packages/effect/src/ai/Decision.ts diff --git a/repos/effect/packages/effect/src/ai/DecisionModel.ts b/packages/effect/src/ai/DecisionModel.ts similarity index 90% rename from repos/effect/packages/effect/src/ai/DecisionModel.ts rename to packages/effect/src/ai/DecisionModel.ts index dd0bf6f8b..9c1d6602c 100644 --- a/repos/effect/packages/effect/src/ai/DecisionModel.ts +++ b/packages/effect/src/ai/DecisionModel.ts @@ -67,6 +67,23 @@ export class DecisionUsage extends Schema.Class( */ export interface DecideOptions { readonly input: Input["Type"] + /** + * Images to include with the input. + */ + readonly images?: ReadonlyArray | undefined +} + +/** + * An image sent to the provider with the encoded input. + * `data` is a base64 string, a byte array, or a URL. + * + * @stability unstable + * @category models + * @since 4.0.1 + */ +export interface Image { + readonly mediaType: string + readonly data: string | Uint8Array | URL } /** @@ -87,6 +104,7 @@ export interface DecideResponse> * Provider input options for a decision request. * `state` is encoded with `Schema.toCodecJson`, not stringified. * All `decisions` must be answered in one call. + * `images` is omitted when no images are supplied. * * @stability unstable * @category options @@ -95,6 +113,7 @@ export interface DecideResponse> export interface ProviderOptions { readonly state: Schema.Json readonly decisions: Record + readonly images?: ReadonlyArray | undefined } /** @@ -325,6 +344,9 @@ const validateAnswers = >( * * **Details** * + * Set `supportsImages: true` to accept images. Otherwise, nonempty image input + * fails with `AiError.InvalidUserInputError`. + * * Providers that round each probability to `probabilityPrecision` decimal * places may return distributions whose sum drifts from 1 by up to half a unit * of the last place per label. Setting `probabilityPrecision` accepts that @@ -341,6 +363,7 @@ const validateAnswers = >( export const make = (params: { readonly decide: (options: ProviderOptions) => Effect.Effect readonly probabilityPrecision?: number | undefined + readonly supportsImages?: boolean | undefined }): Effect.Effect => Effect.sync(() => { const roundingError = params.probabilityPrecision === undefined ? 0 : 0.5 * 10 ** -params.probabilityPrecision @@ -358,7 +381,25 @@ export const make = (params: { reason: new AiError.InvalidUserInputError({ description: error.message }) }) ), - Effect.flatMap((state) => params.decide({ state, decisions: definition.decisions })), + Effect.flatMap((state) => { + const images = options.images?.length ? options.images : undefined + if (images !== undefined && params.supportsImages !== true) { + return Effect.fail( + AiError.make({ + module: "DecisionModel", + method: "decide", + reason: new AiError.InvalidUserInputError({ + description: "This decision model does not accept images" + }) + }) + ) + } + return params.decide({ + state, + decisions: definition.decisions, + ...(images === undefined ? undefined : { images }) + }) + }), Effect.flatMap((response) => Effect.map( validateAnswers(definition.decisions, response.answers, roundingError), diff --git a/repos/effect/packages/effect/src/ai/EmbeddingModel.ts b/packages/effect/src/ai/EmbeddingModel.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/EmbeddingModel.ts rename to packages/effect/src/ai/EmbeddingModel.ts diff --git a/repos/effect/packages/effect/src/ai/IdGenerator.ts b/packages/effect/src/ai/IdGenerator.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/IdGenerator.ts rename to packages/effect/src/ai/IdGenerator.ts diff --git a/repos/effect/packages/effect/src/ai/LanguageModel.ts b/packages/effect/src/ai/LanguageModel.ts similarity index 99% rename from repos/effect/packages/effect/src/ai/LanguageModel.ts rename to packages/effect/src/ai/LanguageModel.ts index 6926ffc93..553bbe9fc 100644 --- a/repos/effect/packages/effect/src/ai/LanguageModel.ts +++ b/packages/effect/src/ai/LanguageModel.ts @@ -869,12 +869,7 @@ export const make: (params: { > => Effect.useSpan( "LanguageModel.generateText", - { - attributes: { - concurrency: options.concurrency, - toolChoice: options.toolChoice - } - }, + { kind: "client" }, Effect.fnUntraced( function*(span) { const spanTransformer = yield* getSpanTransformer @@ -930,11 +925,8 @@ export const make: (params: { return Effect.useSpan( "LanguageModel.generateObject", { - attributes: { - objectName, - concurrency: options.concurrency, - toolChoice: options.toolChoice - } + kind: "client", + attributes: { "effect.ai.object_name": objectName } }, Effect.fnUntraced( function*(span) { @@ -1006,12 +998,7 @@ export const make: (params: { Tools extends Record, Options extends NoExcessProperties, Options> >(options: Options & GenerateTextOptions) { - const span = yield* Effect.makeSpanScoped("LanguageModel.streamText", { - attributes: { - concurrency: options.concurrency, - toolChoice: options.toolChoice - } - }) + const span = yield* Effect.makeSpanScoped("LanguageModel.streamText", { kind: "client" }) const providerOptions: Mutable = { prompt: Prompt.make(options.prompt), @@ -1083,7 +1070,7 @@ export const make: (params: { const tracker = Option.getOrUndefined(yield* Effect.serviceOption(ResponseIdTracker.ResponseIdTracker)) const toolChoice = options.toolChoice ?? "auto" const concurrency = options.concurrency ?? "unbounded" - providerOptions.span.attribute("concurrency", concurrency) + annotateRequestOptions(providerOptions, toolChoice, concurrency) const generateWithNonIncrementalFallback = () => { const requestOptions: ProviderOptions = { @@ -1336,7 +1323,7 @@ export const make: (params: { const tracker = Option.getOrUndefined(yield* Effect.serviceOption(ResponseIdTracker.ResponseIdTracker)) const toolChoice = options.toolChoice ?? "auto" const concurrency = options.concurrency ?? "unbounded" - providerOptions.span.attribute("concurrency", concurrency) + annotateRequestOptions(providerOptions, toolChoice, concurrency) const streamWithNonIncrementalFallback = () => { const requestOptions: ProviderOptions = { @@ -2485,6 +2472,16 @@ const resolveToolkit = , E, R>( ? toolkit : Effect.succeed(toolkit as unknown as Toolkit.WithHandler)) as any +const annotateRequestOptions = ( + { responseFormat, span }: ProviderOptions, + toolChoice: ToolChoice, + concurrency: Concurrency +): void => { + span.attribute("gen_ai.output.type", responseFormat.type) + span.attribute("effect.ai.tool_choice", typeof toolChoice === "string" ? toolChoice : JSON.stringify(toolChoice)) + span.attribute("effect.ai.concurrency", concurrency) +} + /** * @internal */ diff --git a/repos/effect/packages/effect/src/ai/McpProtocol.ts b/packages/effect/src/ai/McpProtocol.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/McpProtocol.ts rename to packages/effect/src/ai/McpProtocol.ts diff --git a/repos/effect/packages/effect/src/ai/McpSchema.ts b/packages/effect/src/ai/McpSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/McpSchema.ts rename to packages/effect/src/ai/McpSchema.ts diff --git a/repos/effect/packages/effect/src/ai/McpServer.ts b/packages/effect/src/ai/McpServer.ts similarity index 95% rename from repos/effect/packages/effect/src/ai/McpServer.ts rename to packages/effect/src/ai/McpServer.ts index f76029cfb..6ed20a37e 100644 --- a/repos/effect/packages/effect/src/ai/McpServer.ts +++ b/packages/effect/src/ai/McpServer.ts @@ -654,6 +654,7 @@ const cancelledResponses = new WeakMap() const requestKey = (requestId: string | number): string => `${typeof requestId}:${requestId}` interface ActiveRequest { + readonly requestId: RpcMessage.RequestId readonly prepared: McpRuntime.PreparedRequest readonly cancelled: boolean } @@ -805,6 +806,7 @@ const runWithRuntime = Effect.fnUntraced(function*( payload: { requestId, reason } }) }) + let writeFromClient!: (clientId: number, message: RpcMessage.FromClientEncoded) => Effect.Effect const handlers = yield* runtime.installHandlers({ core: internalState.get(server)!.core, subscribeServerNotifications: PubSub.subscribe(serverNotifications), @@ -976,8 +978,9 @@ const runWithRuntime = Effect.fnUntraced(function*( } return protocol.send(clientId, response) }, - run: (f) => - protocol.run((clientId, request_) => { + run: (f) => { + writeFromClient = f + return protocol.run((clientId, request_) => { const fiber = Fiber.getCurrent()! const request = request_ as unknown as | RpcMessage.FromServerEncoded @@ -1045,11 +1048,13 @@ const runWithRuntime = Effect.fnUntraced(function*( if (httpRequest !== undefined && session !== undefined) { appendPreResponseHandlerUnsafe(httpRequest, (_, res) => Effect.succeed( - HttpServerResponse.setHeader( - res, - MCP_PROTOCOL_VERSION_HEADER, - session.protocol.protocolVersion - ) + runtime.resolveRequest(clientId, headers) === undefined + ? HttpServerResponse.empty({ status: 404 }) + : HttpServerResponse.setHeader( + res, + MCP_PROTOCOL_VERSION_HEADER, + session.protocol.protocolVersion + ) )) } const routedRequest = runtime.routeClientRequest(selectedProtocol, request) @@ -1130,7 +1135,11 @@ const runWithRuntime = Effect.fnUntraced(function*( } if (request.isNotification !== true) { const requests = activeRequests.get(clientId) ?? new Map() - requests.set(requestKey(request.id), { prepared, cancelled: false }) + requests.set(requestKey(request.id), { + requestId: RpcMessage.RequestId(request.id), + prepared, + cancelled: false + }) activeRequests.set(clientId, requests) } const handled = f(clientId, routedRequest) @@ -1205,7 +1214,22 @@ const runWithRuntime = Effect.fnUntraced(function*( } } }) + } }) + if (isHttp) { + // Stop requests that can no longer receive client replies after termination. + yield* runtime.onSessionTerminated((binding) => { + const interrupts: Array> = [] + for (const [clientId, requests] of activeRequests) { + for (const { prepared, requestId } of requests.values()) { + if (prepared.binding === binding && cancelRequest(clientId, requestId)) { + interrupts.push(writeFromClient(clientId, { _tag: "Interrupt", requestId })) + } + } + } + return Effect.all(interrupts, { discard: true }) + }) + } const { notificationDelivery, notifications } = internalState.get(server)! yield* Effect.acquireRelease( @@ -1526,19 +1550,28 @@ const mcpStdioSerialization = ( * **Details** * * POST serves JSON-RPC and accepted notification-only requests return `202`. - * Modern routing-header mismatches and unsupported protocol versions return - * JSON-RPC errors with status `400`; unknown modern RPC methods return a - * JSON-RPC method-not-found error with status `404`. Unsupported HTTP methods - * return `405`. Requests carrying an `Origin` header are rejected unless the - * exact origin appears in `allowedOrigins`; Origin-less non-browser clients + * Header mismatches, unsupported protocol versions, and invalid session-header + * usage return JSON-RPC errors with status `400`. + * Unknown modern RPC methods return a JSON-RPC method-not-found error with + * status `404`. Session requests without `MCP-Protocol-Version` use the version + * negotiated at initialization. Unsupported HTTP methods return `405`. + * Requests with an `Origin` header are rejected unless the exact origin + * appears in `allowedOrigins`; Origin-less non-browser clients * remain valid. The surrounding HTTP server remains responsible for binding * to an appropriate interface and installing authentication. * + * With `allowSessionTermination`, a DELETE carrying `Mcp-Session-Id` ends that + * session with `204` and interrupts its in-flight requests; later requests with + * that id get `404`. DELETE validates the session and `MCP-Protocol-Version` + * headers like POST. Without the option, or when only sessionless revisions + * such as `v2026_07_28` are configured, DELETE returns `405`. Any caller + * holding a session id can end that session, so authenticate requests in the + * surrounding router. + * * `layerHttp` always implements the single-endpoint Streamable HTTP topology. * Using `v2024_11_05` here is a custom compatibility transport for that * revision's schema. It does not implement the historical two-endpoint - * HTTP+SSE transport, GET SSE, event resumption, session expiry, or client - * session termination. + * HTTP+SSE transport, GET SSE, event resumption, or session expiry. * * @see {@link layerStdio} for exposing the server over stdio * @see {@link layer} for the base MCP server layer without a transport protocol @@ -1558,24 +1591,10 @@ export const layerHttp = (options: { readonly protocols: Arr.NonEmptyReadonlyArray readonly extensions?: ServerExtensions | undefined readonly allowedOrigins?: ReadonlyArray | undefined + readonly allowSessionTermination?: boolean | undefined }): Layer.Layer => { const runtime = McpRuntime.layer(options.protocols) - const methodNotAllowedResponse = HttpServerResponse.empty({ - status: 405, - headers: { allow: "POST" } - }) - const methodNotAllowed = (request: HttpServerRequest.HttpServerRequest) => - isAllowedMcpOrigin(request, options.allowedOrigins) - ? Effect.succeed(methodNotAllowedResponse) - : Effect.succeed(HttpServerResponse.empty({ status: 403 })) - const routes = Layer.mergeAll( - HttpRouter.add("GET", options.path, methodNotAllowed), - HttpRouter.add("PUT", options.path, methodNotAllowed), - HttpRouter.add("PATCH", options.path, methodNotAllowed), - HttpRouter.add("DELETE", options.path, methodNotAllowed), - HttpRouter.add("OPTIONS", options.path, methodNotAllowed) - ) - return Layer.merge(layerWithRuntime(options, "http"), routes).pipe( + return layerWithRuntime(options, "http").pipe( Layer.provide(layerMcpProtocolHttp(options)), Layer.provide(runtime), Layer.provide(RpcSerialization.layerJsonRpc()) @@ -1585,6 +1604,7 @@ export const layerHttp = (options: { const layerMcpProtocolHttp = (options: { readonly path: HttpRouter.PathInput readonly allowedOrigins?: ReadonlyArray | undefined + readonly allowSessionTermination?: boolean | undefined }): Layer.Layer< RpcServer.Protocol, never, @@ -1596,6 +1616,29 @@ const layerMcpProtocolHttp = (options: { Effect.provideService(RpcSerialization.RpcSerialization, mcpHttpSerialization) ) const router = yield* HttpRouter.HttpRouter + const allowSessionTermination = options.allowSessionTermination === true && + runtime.protocols.some((protocol) => protocol.runtime._tag === "Stateful") + const forbidden = Effect.succeed(HttpServerResponse.empty({ status: 403 })) + const withAllowedOrigin = ( + handler: (request: HttpServerRequest.HttpServerRequest) => Effect.Effect + ) => + (request: HttpServerRequest.HttpServerRequest) => + isAllowedMcpOrigin(request, options.allowedOrigins) ? handler(request) : forbidden + const methodNotAllowedResponse = Effect.succeed(HttpServerResponse.empty({ + status: 405, + headers: { allow: allowSessionTermination ? "POST, DELETE" : "POST" } + })) + const methodNotAllowed = withAllowedOrigin(() => methodNotAllowedResponse) + for (const method of ["GET", "PUT", "PATCH", "OPTIONS"] as const) { + yield* router.add(method, options.path, methodNotAllowed) + } + yield* router.add( + "DELETE", + options.path, + allowSessionTermination + ? withAllowedOrigin((request) => runtime.terminateHttpSession(request.headers)) + : methodNotAllowed + ) yield* router.add("POST", options.path, (request) => { if (!isAllowedMcpOrigin(request, options.allowedOrigins)) { return Effect.succeed(HttpServerResponse.empty({ status: 403 })) @@ -1809,7 +1852,7 @@ export const registerToolkit: >( ) => Effect.Effect< void, never, - McpServer | Tool.HandlersFor | Exclude, McpRequestContext> + McpServer | Tool.HandlersFor | Exclude, McpRequestContext> > = Effect.fnUntraced(function*>( toolkit: Toolkit.Toolkit ) { @@ -1829,7 +1872,11 @@ export const registerToolkit: >( } return Context.makeUnsafe(services) })) - const services = omitRequestServices(yield* Effect.context()) + const services = omitRequestServices( + yield* Effect.context< + Exclude, McpRequestContext> + >() + ) const reportCause = (cause: Cause.Cause) => Effect.provideContext(ErrorReporter.report(cause), services) // Interruption propagates; anything else is logged, reported and scrubbed. const internalToolError = (cause: Cause.Cause) => { @@ -1973,7 +2020,7 @@ export const toolkit = >( ): Layer.Layer< never, never, - Tool.HandlersFor | Exclude, McpRequestContext> + Tool.HandlersFor | Exclude, McpRequestContext> > => Layer.effectDiscard(registerToolkit(toolkit)).pipe( Layer.provide(McpServer.layer) @@ -1997,6 +2044,19 @@ export type ValidateCompletions = : never } +/** + * Utility type that collects the services required by the handlers of a + * completion-handler record, including handlers declared as optional. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ +export type CompletionServices = { + [K in keyof Completions]-?: NonNullable extends (...args: any) => infer Ret ? Effect.Services + : never +}[keyof Completions] + /** * Completion-handler map for a resource URI template. * @@ -2314,7 +2374,13 @@ export const registerPrompt = < ): Effect.Effect< void, never, - Exclude | R, McpRequestContext> | McpServer + | Exclude< + | Schema.Struct.DecodingServices + | R + | CompletionServices, + McpRequestContext + > + | McpServer > => { const args = Arr.empty() const props: Record = options.parameters ?? {} @@ -2452,7 +2518,12 @@ export const prompt = < ): Layer.Layer< never, never, - Exclude | R, McpRequestContext> + Exclude< + | Schema.Struct.DecodingServices + | R + | CompletionServices, + McpRequestContext + > > => Layer.effectDiscard(registerPrompt(options)).pipe( Layer.provide(McpServer.layer) diff --git a/repos/effect/packages/effect/src/ai/Model.ts b/packages/effect/src/ai/Model.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Model.ts rename to packages/effect/src/ai/Model.ts diff --git a/repos/effect/packages/effect/src/ai/OpenAiStructuredOutput.ts b/packages/effect/src/ai/OpenAiStructuredOutput.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/OpenAiStructuredOutput.ts rename to packages/effect/src/ai/OpenAiStructuredOutput.ts diff --git a/repos/effect/packages/effect/src/ai/Prompt.ts b/packages/effect/src/ai/Prompt.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Prompt.ts rename to packages/effect/src/ai/Prompt.ts diff --git a/repos/effect/packages/effect/src/ai/Response.ts b/packages/effect/src/ai/Response.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Response.ts rename to packages/effect/src/ai/Response.ts diff --git a/repos/effect/packages/effect/src/ai/ResponseIdTracker.ts b/packages/effect/src/ai/ResponseIdTracker.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/ResponseIdTracker.ts rename to packages/effect/src/ai/ResponseIdTracker.ts diff --git a/repos/effect/packages/effect/src/ai/Telemetry.ts b/packages/effect/src/ai/Telemetry.ts similarity index 86% rename from repos/effect/packages/effect/src/ai/Telemetry.ts rename to packages/effect/src/ai/Telemetry.ts index 27fda52e2..4b39a2c71 100644 --- a/repos/effect/packages/effect/src/ai/Telemetry.ts +++ b/packages/effect/src/ai/Telemetry.ts @@ -27,50 +27,35 @@ import type * as Response from "./Response.ts" * * These attributes follow the OpenTelemetry generative AI semantic * conventions: - * https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/ + * https://github.com/open-telemetry/semantic-conventions-genai * * @stability unstable * @category models * @since 4.0.0 */ export type GenAITelemetryAttributes = Struct.Simplify< - & AttributesWithPrefix + & AttributesWithPrefix & AttributesWithPrefix - & AttributesWithPrefix + & AttributesWithPrefix & AttributesWithPrefix & AttributesWithPrefix & AttributesWithPrefix > -/** - * All telemetry attributes which are part of the GenAI specification. - * - * @stability unstable - * @category models - * @since 4.0.0 - */ -export type AllAttributes = - & BaseAttributes - & OperationAttributes - & TokenAttributes - & UsageAttributes - & RequestAttributes - & ResponseAttributes - /** * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai`. + * namespaced by `gen_ai.provider`. * * @stability unstable * @category models - * @since 4.0.0 + * @since 4.0.2 */ -export interface BaseAttributes { +export interface ProviderAttributes { /** - * The Generative AI product as identified by the client or server - * instrumentation. + * The Generative AI provider as identified by the client or server + * instrumentation, written as `gen_ai.provider.name`. */ - readonly system?: (string & {}) | WellKnownSystem | null | undefined + readonly name?: (string & {}) | WellKnownProviderName | null | undefined } /** @@ -87,14 +72,17 @@ export interface OperationAttributes { /** * Telemetry attributes which are part of the GenAI specification and are - * namespaced by `gen_ai.token`. + * namespaced by `gen_ai.output`. * * @stability unstable * @category models - * @since 4.0.0 + * @since 4.0.2 */ -export interface TokenAttributes { - readonly type?: string | null | undefined +export interface OutputAttributes { + /** + * The output modality requested by the client. + */ + readonly type?: (string & {}) | WellKnownOutputType | null | undefined } /** @@ -128,15 +116,15 @@ export interface RequestAttributes { */ readonly temperature?: number | null | undefined /** - * The temperature setting for the GenAI request. + * The top_k sampling setting for the GenAI request. */ readonly topK?: number | null | undefined /** - * The top_k sampling setting for the GenAI request. + * The top_p sampling setting for the GenAI request. */ readonly topP?: number | null | undefined /** - * The top_p sampling setting for the GenAI request. + * The maximum number of tokens the model may generate. */ readonly maxTokens?: number | null | undefined /** @@ -203,7 +191,18 @@ export interface ResponseAttributes { export type WellKnownOperationName = "chat" | "embeddings" | "text_completion" /** - * The `gen_ai.system` attribute has the following list of well-known values. + * The `gen_ai.output.type` attribute has the following list of well-known + * values. + * + * @stability unstable + * @category models + * @since 4.0.2 + */ +export type WellKnownOutputType = "text" | "json" | "image" | "speech" + +/** + * The `gen_ai.provider.name` attribute has the following list of well-known + * values. * * **Details** * @@ -212,23 +211,25 @@ export type WellKnownOperationName = "chat" | "embeddings" | "text_completion" * * @stability unstable * @category models - * @since 4.0.0 + * @since 4.0.2 */ -export type WellKnownSystem = +export type WellKnownProviderName = | "anthropic" | "aws.bedrock" - | "az.ai.inference" - | "az.ai.openai" + | "azure.ai.inference" + | "azure.ai.openai" | "cohere" | "deepseek" - | "gemini" + | "gcp.gemini" + | "gcp.gen_ai" + | "gcp.vertex_ai" | "groq" | "ibm.watsonx.ai" | "mistral_ai" + | "moonshot_ai" | "openai" | "perplexity" - | "vertex_ai" - | "xai" + | "x_ai" /** * Utility type for prefixing attribute names with a namespace. @@ -375,11 +376,11 @@ export const addSpanAttributes = ( } } -const addSpanBaseAttributes = addSpanAttributes("gen_ai", String.camelToSnake) +const addSpanProviderAttributes = addSpanAttributes("gen_ai.provider", String.camelToSnake) const addSpanOperationAttributes = addSpanAttributes("gen_ai.operation", String.camelToSnake) +const addSpanOutputAttributes = addSpanAttributes("gen_ai.output", String.camelToSnake) const addSpanRequestAttributes = addSpanAttributes("gen_ai.request", String.camelToSnake) const addSpanResponseAttributes = addSpanAttributes("gen_ai.response", String.camelToSnake) -const addSpanTokenAttributes = addSpanAttributes("gen_ai.token", String.camelToSnake) const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToSnake) /** @@ -387,8 +388,7 @@ const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToS * * **Details** * - * Combines base attributes with optional grouped attributes for comprehensive - * telemetry coverage of AI operations. + * Groups attributes by their `gen_ai.*` namespace. * * **Example** (Configuring GenAI telemetry attributes) * @@ -396,7 +396,7 @@ const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToS * import type { Telemetry } from "effect/ai" * * const telemetryOptions: Telemetry.GenAITelemetryAttributeOptions = { - * system: "openai", + * provider: { name: "openai" }, * operation: { * name: "chat" * }, @@ -416,18 +416,26 @@ const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToS * } * } * - * const result = [telemetryOptions.system, telemetryOptions.usage?.inputTokens] // => ["openai", 50] + * const result = [telemetryOptions.provider?.name, telemetryOptions.usage?.inputTokens] // => ["openai", 50] * ``` * * @stability unstable * @category options * @since 4.0.0 */ -export type GenAITelemetryAttributeOptions = BaseAttributes & { +export type GenAITelemetryAttributeOptions = { + /** + * Provider attributes (e.g., provider name). + */ + readonly provider?: ProviderAttributes | undefined /** * Operation-specific attributes (e.g., operation name). */ readonly operation?: OperationAttributes | undefined + /** + * Output attributes (e.g., requested output type). + */ + readonly output?: OutputAttributes | undefined /** * Request-specific attributes (e.g., model parameters). */ @@ -436,10 +444,6 @@ export type GenAITelemetryAttributeOptions = BaseAttributes & { * Response-specific attributes (e.g., response metadata). */ readonly response?: ResponseAttributes | undefined - /** - * Token-specific attributes. - */ - readonly token?: TokenAttributes | undefined /** * Usage statistics attributes (e.g., token counts). */ @@ -451,7 +455,7 @@ export type GenAITelemetryAttributeOptions = BaseAttributes & { * * **When to use** * - * Use when you need to write GenAI request, response, token, or usage + * Use when you need to write GenAI request, response, or usage * attributes onto an existing OpenTelemetry span. * * **Details** @@ -473,7 +477,7 @@ export type GenAITelemetryAttributeOptions = BaseAttributes & { * const span = yield* Effect.currentSpan * * Telemetry.addGenAIAnnotations(span, { - * system: "openai", + * provider: { name: "openai" }, * request: { model: "gpt-4", temperature: 0.7 }, * usage: { inputTokens: 100, outputTokens: 50 } * }) @@ -491,11 +495,11 @@ export const addGenAIAnnotations: { (options: GenAITelemetryAttributeOptions): (span: Span) => void (span: Span, options: GenAITelemetryAttributeOptions): void } = dual(2, (span: Span, options: GenAITelemetryAttributeOptions) => { - addSpanBaseAttributes(span, { system: options.system }) + if (Predicate.isNotNullish(options.provider)) addSpanProviderAttributes(span, options.provider) if (Predicate.isNotNullish(options.operation)) addSpanOperationAttributes(span, options.operation) + if (Predicate.isNotNullish(options.output)) addSpanOutputAttributes(span, options.output) if (Predicate.isNotNullish(options.request)) addSpanRequestAttributes(span, options.request) if (Predicate.isNotNullish(options.response)) addSpanResponseAttributes(span, options.response) - if (Predicate.isNotNullish(options.token)) addSpanTokenAttributes(span, options.token) if (Predicate.isNotNullish(options.usage)) addSpanUsageAttributes(span, options.usage) }) diff --git a/repos/effect/packages/effect/src/ai/Tokenizer.ts b/packages/effect/src/ai/Tokenizer.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Tokenizer.ts rename to packages/effect/src/ai/Tokenizer.ts diff --git a/repos/effect/packages/effect/src/ai/Tool.ts b/packages/effect/src/ai/Tool.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Tool.ts rename to packages/effect/src/ai/Tool.ts diff --git a/repos/effect/packages/effect/src/ai/Toolkit.ts b/packages/effect/src/ai/Toolkit.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/Toolkit.ts rename to packages/effect/src/ai/Toolkit.ts diff --git a/repos/effect/packages/effect/src/ai/index.ts b/packages/effect/src/ai/index.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/index.ts rename to packages/effect/src/ai/index.ts diff --git a/repos/effect/packages/effect/src/ai/internal/codec-transformer.ts b/packages/effect/src/ai/internal/codec-transformer.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/codec-transformer.ts rename to packages/effect/src/ai/internal/codec-transformer.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpCore.ts b/packages/effect/src/ai/internal/mcpCore.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpCore.ts rename to packages/effect/src/ai/internal/mcpCore.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol.ts b/packages/effect/src/ai/internal/mcpProtocol.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol.ts rename to packages/effect/src/ai/internal/mcpProtocol.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts b/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts rename to packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts b/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts rename to packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts b/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts rename to packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts b/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts rename to packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts b/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts rename to packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocolRegistry.ts b/packages/effect/src/ai/internal/mcpProtocolRegistry.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpProtocolRegistry.ts rename to packages/effect/src/ai/internal/mcpProtocolRegistry.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts b/packages/effect/src/ai/internal/mcpRuntime.ts similarity index 86% rename from repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts rename to packages/effect/src/ai/internal/mcpRuntime.ts index b388d1780..dbf891211 100644 --- a/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts +++ b/packages/effect/src/ai/internal/mcpRuntime.ts @@ -17,6 +17,7 @@ import * as Predicate from "../../Predicate.ts" import * as Result from "../../Result.ts" import * as RpcGroup from "../../rpc/RpcGroup.ts" import type * as RpcMessage from "../../rpc/RpcMessage.ts" +import type * as Scope from "../../Scope.ts" import type * as PublicMcpProtocol from "../McpProtocol.ts" import * as PublicMcpSchema from "../McpSchema.ts" import type * as McpCore from "./mcpCore.ts" @@ -175,6 +176,10 @@ export interface ServerRuntimeShape { fallback: LogLevel.LogLevel ) => LogLevel.LogLevel readonly disconnect: (clientId: number) => void + readonly terminateHttpSession: (headers: Headers.Headers) => Effect.Effect + readonly onSessionTerminated: ( + listener: (binding: RequestBinding) => Effect.Effect + ) => Effect.Effect readonly deliveryClientIds: () => Iterable readonly canDeliver: ( clientId: number, @@ -228,9 +233,36 @@ export const make = Effect.fnUntraced(function*( statelessProtocol = protocol } const registry = yield* McpProtocolRegistry.make(protocols) - const selectHttpProtocol = (headers: Headers.Headers, input: unknown): HttpProtocolSelection => { + const sessionTerminationListeners = new Set<(binding: RequestBinding) => Effect.Effect>() + const unsupportedProtocolVersion = (requested: string) => ({ + code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, + message: `Unsupported protocol version '${requested}'`, + data: { supported: protocolVersions, requested } + }) + const selectHttpSession = (headers: Headers.Headers, isInitialize: boolean): HttpProtocolSelection => { const protocolVersion = headers[MCP_PROTOCOL_VERSION_HEADER] const sessionId = headers[MCP_SESSION_ID_HEADER] + const binding = sessionId === undefined ? undefined : stateful?.resolveSessionId(sessionId) + if (sessionId !== undefined && binding === undefined) { + return { _tag: "Rejected", status: 404 } + } + if (!isInitialize && protocolVersion !== undefined) { + if (!protocolVersions.includes(protocolVersion)) { + return { _tag: "Rejected", status: 400, error: unsupportedProtocolVersion(protocolVersion) } + } + if ( + binding?.protocol.runtime.transport.http.requiresVersionHeader === true && + protocolVersion !== binding.protocol.protocolVersion + ) { + return headerMismatch( + `MCP-Protocol-Version header '${protocolVersion}' does not match negotiated protocol version '${binding.protocol.protocolVersion}'` + ) + } + } + return { _tag: "Accepted", binding, protocol: binding?.protocol } + } + const selectHttpProtocol = (headers: Headers.Headers, input: unknown): HttpProtocolSelection => { + const protocolVersion = headers[MCP_PROTOCOL_VERSION_HEADER] const inputRecord = asRecord(input) const metadata = asRecord(asRecord(inputRecord?.params)?._meta) const claim = protocolVersionClaim(metadata) @@ -272,18 +304,7 @@ export const make = Effect.fnUntraced(function*( } } if (statelessProtocol === undefined || protocolVersion !== statelessProtocol.protocolVersion) { - return { - _tag: "Rejected", - status: 400, - error: { - code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, - message: `Unsupported protocol version '${protocolVersion}'`, - data: { - supported: protocolVersions, - requested: protocolVersion - } - } - } + return { _tag: "Rejected", status: 400, error: unsupportedProtocolVersion(protocolVersion) } } const method = inputRecord?.method if (typeof method !== "string" || headers[MCP_METHOD_HEADER] !== method) { @@ -299,25 +320,7 @@ export const make = Effect.fnUntraced(function*( } return { _tag: "Accepted", binding: undefined, protocol: statelessProtocol } } - const binding = sessionId === undefined ? undefined : stateful?.resolveSessionId(sessionId) - if (sessionId !== undefined && binding === undefined) { - return { _tag: "Rejected", status: 404 } - } - if ( - !isInitialize && - protocolVersion !== undefined && - !registry.protocols.some((protocol) => protocol.protocolVersion === protocolVersion) - ) { - return { _tag: "Rejected", status: 400 } - } - if ( - !isInitialize && - binding?.protocol.runtime.transport.http.requiresVersionHeader === true && - protocolVersion !== binding.protocol.protocolVersion - ) { - return { _tag: "Rejected", status: 400 } - } - return { _tag: "Accepted", binding, protocol: binding?.protocol } + return selectHttpSession(headers, isInitialize) } return ServerRuntime.of({ protocols: registry.protocols, @@ -362,11 +365,7 @@ export const make = Effect.fnUntraced(function*( return yield* Effect.die("MCP stateless runtime invariant failed") } if (requestedVersion !== undefined && requestedVersion !== protocol.protocolVersion) { - return yield* new McpProtocol.ProtocolError({ - code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, - message: `Unsupported protocol version '${requestedVersion}'`, - data: { supported: protocolVersions, requested: requestedVersion } - }) + return yield* new McpProtocol.ProtocolError(unsupportedProtocolVersion(requestedVersion)) } const decodedProfile = yield* statelessDescriptor.profileFromRequestMetadata(metadata) const profile: McpCore.NegotiatedProtocolProfile = { @@ -394,12 +393,14 @@ export const make = Effect.fnUntraced(function*( }) if (Result.isFailure(parsed)) { const version = headers[MCP_PROTOCOL_VERSION_HEADER] - if (version !== undefined && !registry.protocols.some((protocol) => protocol.protocolVersion === version)) { - return reject(400) + if (version !== undefined && !protocolVersions.includes(version)) { + return reject(400, unsupportedProtocolVersion(version)) } const admission = selectHttpProtocol(headers, undefined) - return admission._tag === "Rejected" && admission.error === undefined - ? reject(admission.status) + // Session and protocol-header errors take precedence over parse errors. + return admission._tag === "Rejected" && + (admission.error === undefined || admission.error.code === PublicMcpSchema.HEADER_MISMATCH_ERROR_CODE) + ? reject(admission.status, admission.error) : reject(200, new PublicMcpSchema.ParseError({ message: "Parse error" })) } const input = parsed.success @@ -444,8 +445,21 @@ export const make = Effect.fnUntraced(function*( } const isInitialize = Predicate.hasProperty(input, "method") && input.method === "initialize" const hasSession = headers[MCP_SESSION_ID_HEADER] !== undefined - if (isInitialize ? hasSession : !hasSession && admission.protocol?.runtime._tag !== "Stateless") { - return reject(400) + if (isInitialize && hasSession) { + return reject( + 400, + new PublicMcpSchema.InvalidRequest({ message: "initialize must not include an MCP-Session-Id header" }), + id + ) + } + if (!isInitialize && !hasSession && admission.protocol?.runtime._tag !== "Stateless") { + return reject( + 400, + stateful === undefined ? undefined : new PublicMcpSchema.InvalidRequest({ + message: "MCP-Session-Id header is required; send initialize to start a session" + }), + id + ) } if ( isRequest && admission.protocol?.runtime._tag === "Stateless" && @@ -464,6 +478,33 @@ export const make = Effect.fnUntraced(function*( }, effectLogLevel: (clientId, headers, fallback) => stateful?.effectLogLevel(clientId, headers, fallback) ?? fallback, disconnect: (clientId) => stateful?.disconnect(clientId), + terminateHttpSession: (headers) => + Effect.suspend(() => { + const sessionId = headers[MCP_SESSION_ID_HEADER] + if (sessionId === undefined) { + return Effect.succeed(HttpServerResponse.empty({ status: 400 })) + } + const selection = selectHttpSession(headers, false) + if (selection._tag === "Rejected") { + return Effect.succeed(HttpServerResponse.empty({ status: selection.status })) + } + const binding = selection.binding! + stateful!.terminateSession(sessionId) + return Effect.as( + Effect.forEach(sessionTerminationListeners, (listener) => listener(binding), { discard: true }), + HttpServerResponse.empty({ status: 204 }) + ) + }), + onSessionTerminated: (listener) => + Effect.acquireRelease( + Effect.sync(() => { + sessionTerminationListeners.add(listener) + }), + () => + Effect.sync(() => { + sessionTerminationListeners.delete(listener) + }) + ), deliveryClientIds: () => stateful?.initializedClientIds() ?? [], canDeliver: (clientId, headers, notification, fallback) => stateful?.canDeliver(clientId, headers, notification, fallback) ?? true, diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts b/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts rename to packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts b/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts rename to packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts b/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts rename to packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts b/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts rename to packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts b/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts rename to packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts diff --git a/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts b/packages/effect/src/ai/internal/mcpStatefulRuntime.ts similarity index 98% rename from repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts rename to packages/effect/src/ai/internal/mcpStatefulRuntime.ts index 19c41ab07..bcede3100 100644 --- a/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts +++ b/packages/effect/src/ai/internal/mcpStatefulRuntime.ts @@ -47,6 +47,7 @@ export interface StatefulRuntime { readonly registerConnection: (clientId: number, registration: Registration) => Binding readonly resolve: (clientId: number, headers: Headers.Headers) => Binding | undefined readonly resolveSessionId: (sessionId: string) => Binding | undefined + readonly terminateSession: (sessionId: string) => boolean readonly setLogLevel: ( level: PublicMcpSchema.LoggingLevel, clientId: number, @@ -123,6 +124,7 @@ export const make = (): StatefulRuntime => { }, resolve: resolveSession, resolveSessionId: (sessionId) => bySessionId.get(sessionId), + terminateSession: (sessionId) => bySessionId.delete(sessionId), setLogLevel: (level, clientId, headers) => Effect.sync(() => { const session = resolveSession(clientId, headers) diff --git a/repos/effect/packages/effect/src/ai/internal/structured-output.ts b/packages/effect/src/ai/internal/structured-output.ts similarity index 100% rename from repos/effect/packages/effect/src/ai/internal/structured-output.ts rename to packages/effect/src/ai/internal/structured-output.ts diff --git a/repos/effect/packages/effect/src/cli/Argument.ts b/packages/effect/src/cli/Argument.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Argument.ts rename to packages/effect/src/cli/Argument.ts diff --git a/repos/effect/packages/effect/src/cli/CliConfig.ts b/packages/effect/src/cli/CliConfig.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/CliConfig.ts rename to packages/effect/src/cli/CliConfig.ts diff --git a/repos/effect/packages/effect/src/cli/CliError.ts b/packages/effect/src/cli/CliError.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/CliError.ts rename to packages/effect/src/cli/CliError.ts diff --git a/repos/effect/packages/effect/src/cli/CliOutput.ts b/packages/effect/src/cli/CliOutput.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/CliOutput.ts rename to packages/effect/src/cli/CliOutput.ts diff --git a/repos/effect/packages/effect/src/cli/Command.ts b/packages/effect/src/cli/Command.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Command.ts rename to packages/effect/src/cli/Command.ts diff --git a/repos/effect/packages/effect/src/cli/Completions.ts b/packages/effect/src/cli/Completions.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Completions.ts rename to packages/effect/src/cli/Completions.ts diff --git a/repos/effect/packages/effect/src/cli/Flag.ts b/packages/effect/src/cli/Flag.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Flag.ts rename to packages/effect/src/cli/Flag.ts diff --git a/repos/effect/packages/effect/src/cli/GlobalFlag.ts b/packages/effect/src/cli/GlobalFlag.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/GlobalFlag.ts rename to packages/effect/src/cli/GlobalFlag.ts diff --git a/repos/effect/packages/effect/src/cli/HelpDoc.ts b/packages/effect/src/cli/HelpDoc.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/HelpDoc.ts rename to packages/effect/src/cli/HelpDoc.ts diff --git a/repos/effect/packages/effect/src/cli/Param.ts b/packages/effect/src/cli/Param.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Param.ts rename to packages/effect/src/cli/Param.ts diff --git a/repos/effect/packages/effect/src/cli/Primitive.ts b/packages/effect/src/cli/Primitive.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Primitive.ts rename to packages/effect/src/cli/Primitive.ts diff --git a/repos/effect/packages/effect/src/cli/Prompt.ts b/packages/effect/src/cli/Prompt.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Prompt.ts rename to packages/effect/src/cli/Prompt.ts diff --git a/repos/effect/packages/effect/src/cli/index.ts b/packages/effect/src/cli/index.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/index.ts rename to packages/effect/src/cli/index.ts diff --git a/repos/effect/packages/effect/src/cli/internal/ansi.ts b/packages/effect/src/cli/internal/ansi.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/ansi.ts rename to packages/effect/src/cli/internal/ansi.ts diff --git a/repos/effect/packages/effect/src/cli/internal/auto-suggest.ts b/packages/effect/src/cli/internal/auto-suggest.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/auto-suggest.ts rename to packages/effect/src/cli/internal/auto-suggest.ts diff --git a/repos/effect/packages/effect/src/cli/internal/command.ts b/packages/effect/src/cli/internal/command.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/command.ts rename to packages/effect/src/cli/internal/command.ts diff --git a/repos/effect/packages/effect/src/cli/internal/completions/bash.ts b/packages/effect/src/cli/internal/completions/bash.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/completions/bash.ts rename to packages/effect/src/cli/internal/completions/bash.ts diff --git a/repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts b/packages/effect/src/cli/internal/completions/descriptor.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts rename to packages/effect/src/cli/internal/completions/descriptor.ts diff --git a/repos/effect/packages/effect/src/cli/internal/completions/fish.ts b/packages/effect/src/cli/internal/completions/fish.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/completions/fish.ts rename to packages/effect/src/cli/internal/completions/fish.ts diff --git a/repos/effect/packages/effect/src/cli/internal/completions/zsh.ts b/packages/effect/src/cli/internal/completions/zsh.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/completions/zsh.ts rename to packages/effect/src/cli/internal/completions/zsh.ts diff --git a/repos/effect/packages/effect/src/cli/internal/config.ts b/packages/effect/src/cli/internal/config.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/config.ts rename to packages/effect/src/cli/internal/config.ts diff --git a/repos/effect/packages/effect/src/cli/internal/help.ts b/packages/effect/src/cli/internal/help.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/help.ts rename to packages/effect/src/cli/internal/help.ts diff --git a/repos/effect/packages/effect/src/cli/internal/lexer.ts b/packages/effect/src/cli/internal/lexer.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/lexer.ts rename to packages/effect/src/cli/internal/lexer.ts diff --git a/repos/effect/packages/effect/src/cli/internal/parser.ts b/packages/effect/src/cli/internal/parser.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/parser.ts rename to packages/effect/src/cli/internal/parser.ts diff --git a/repos/effect/packages/effect/src/cli/internal/wizard.ts b/packages/effect/src/cli/internal/wizard.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/wizard.ts rename to packages/effect/src/cli/internal/wizard.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterCron.ts b/packages/effect/src/cluster/ClusterCron.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterCron.ts rename to packages/effect/src/cluster/ClusterCron.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterError.ts b/packages/effect/src/cluster/ClusterError.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterError.ts rename to packages/effect/src/cluster/ClusterError.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterMetrics.ts b/packages/effect/src/cluster/ClusterMetrics.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterMetrics.ts rename to packages/effect/src/cluster/ClusterMetrics.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterSchema.ts b/packages/effect/src/cluster/ClusterSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterSchema.ts rename to packages/effect/src/cluster/ClusterSchema.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts b/packages/effect/src/cluster/ClusterWorkflowEngine.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts rename to packages/effect/src/cluster/ClusterWorkflowEngine.ts diff --git a/repos/effect/packages/effect/src/cluster/DeliverAt.ts b/packages/effect/src/cluster/DeliverAt.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/DeliverAt.ts rename to packages/effect/src/cluster/DeliverAt.ts diff --git a/repos/effect/packages/effect/src/cluster/Entity.ts b/packages/effect/src/cluster/Entity.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Entity.ts rename to packages/effect/src/cluster/Entity.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityAddress.ts b/packages/effect/src/cluster/EntityAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityAddress.ts rename to packages/effect/src/cluster/EntityAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityId.ts b/packages/effect/src/cluster/EntityId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityId.ts rename to packages/effect/src/cluster/EntityId.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityProxy.ts b/packages/effect/src/cluster/EntityProxy.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityProxy.ts rename to packages/effect/src/cluster/EntityProxy.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityProxyServer.ts b/packages/effect/src/cluster/EntityProxyServer.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityProxyServer.ts rename to packages/effect/src/cluster/EntityProxyServer.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityResource.ts b/packages/effect/src/cluster/EntityResource.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityResource.ts rename to packages/effect/src/cluster/EntityResource.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityType.ts b/packages/effect/src/cluster/EntityType.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityType.ts rename to packages/effect/src/cluster/EntityType.ts diff --git a/repos/effect/packages/effect/src/cluster/Envelope.ts b/packages/effect/src/cluster/Envelope.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Envelope.ts rename to packages/effect/src/cluster/Envelope.ts diff --git a/repos/effect/packages/effect/src/cluster/HttpRunner.ts b/packages/effect/src/cluster/HttpRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/HttpRunner.ts rename to packages/effect/src/cluster/HttpRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts b/packages/effect/src/cluster/K8sHttpClient.ts similarity index 96% rename from repos/effect/packages/effect/src/cluster/K8sHttpClient.ts rename to packages/effect/src/cluster/K8sHttpClient.ts index 6e0a44159..7b5538053 100644 --- a/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts +++ b/packages/effect/src/cluster/K8sHttpClient.ts @@ -56,11 +56,21 @@ export const layer: Layer.Layer< Effect.gen(function*() { const fs = yield* FileSystem.FileSystem const token = yield* fs.readFileString("/var/run/secrets/kubernetes.io/serviceaccount/token").pipe( - Effect.option + Effect.map((token) => token.trim()), + Effect.option, + Effect.cachedWithTTL("1 minute") ) return (yield* HttpClient.HttpClient).pipe( HttpClient.mapRequest(HttpClientRequest.prependUrl("https://kubernetes.default.svc/api")), - token._tag === "Some" ? HttpClient.mapRequest(HttpClientRequest.bearerToken(token.value.trim())) : identity, + HttpClient.mapRequestEffect((request) => + Effect.map( + token, + Option.match({ + onNone: () => request, + onSome: (token) => HttpClientRequest.bearerToken(request, token) + }) + ) + ), HttpClient.filterStatusOk, HttpClient.retryTransient({ schedule: Schedule.spaced(5000) diff --git a/repos/effect/packages/effect/src/cluster/K8sTypes.ts b/packages/effect/src/cluster/K8sTypes.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/K8sTypes.ts rename to packages/effect/src/cluster/K8sTypes.ts diff --git a/repos/effect/packages/effect/src/cluster/MachineId.ts b/packages/effect/src/cluster/MachineId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/MachineId.ts rename to packages/effect/src/cluster/MachineId.ts diff --git a/repos/effect/packages/effect/src/cluster/Message.ts b/packages/effect/src/cluster/Message.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Message.ts rename to packages/effect/src/cluster/Message.ts diff --git a/repos/effect/packages/effect/src/cluster/MessageStorage.ts b/packages/effect/src/cluster/MessageStorage.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/MessageStorage.ts rename to packages/effect/src/cluster/MessageStorage.ts diff --git a/repos/effect/packages/effect/src/cluster/Reply.ts b/packages/effect/src/cluster/Reply.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Reply.ts rename to packages/effect/src/cluster/Reply.ts diff --git a/repos/effect/packages/effect/src/cluster/Runner.ts b/packages/effect/src/cluster/Runner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Runner.ts rename to packages/effect/src/cluster/Runner.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerAddress.ts b/packages/effect/src/cluster/RunnerAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerAddress.ts rename to packages/effect/src/cluster/RunnerAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerHealth.ts b/packages/effect/src/cluster/RunnerHealth.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerHealth.ts rename to packages/effect/src/cluster/RunnerHealth.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerServer.ts b/packages/effect/src/cluster/RunnerServer.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerServer.ts rename to packages/effect/src/cluster/RunnerServer.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerStorage.ts b/packages/effect/src/cluster/RunnerStorage.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerStorage.ts rename to packages/effect/src/cluster/RunnerStorage.ts diff --git a/repos/effect/packages/effect/src/cluster/Runners.ts b/packages/effect/src/cluster/Runners.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Runners.ts rename to packages/effect/src/cluster/Runners.ts diff --git a/repos/effect/packages/effect/src/cluster/ShardId.ts b/packages/effect/src/cluster/ShardId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardId.ts rename to packages/effect/src/cluster/ShardId.ts diff --git a/repos/effect/packages/effect/src/cluster/Sharding.ts b/packages/effect/src/cluster/Sharding.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Sharding.ts rename to packages/effect/src/cluster/Sharding.ts diff --git a/repos/effect/packages/effect/src/cluster/ShardingConfig.ts b/packages/effect/src/cluster/ShardingConfig.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardingConfig.ts rename to packages/effect/src/cluster/ShardingConfig.ts diff --git a/repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts b/packages/effect/src/cluster/ShardingRegistrationEvent.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts rename to packages/effect/src/cluster/ShardingRegistrationEvent.ts diff --git a/repos/effect/packages/effect/src/cluster/SingleRunner.ts b/packages/effect/src/cluster/SingleRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SingleRunner.ts rename to packages/effect/src/cluster/SingleRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/Singleton.ts b/packages/effect/src/cluster/Singleton.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Singleton.ts rename to packages/effect/src/cluster/Singleton.ts diff --git a/repos/effect/packages/effect/src/cluster/SingletonAddress.ts b/packages/effect/src/cluster/SingletonAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SingletonAddress.ts rename to packages/effect/src/cluster/SingletonAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/Snowflake.ts b/packages/effect/src/cluster/Snowflake.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Snowflake.ts rename to packages/effect/src/cluster/Snowflake.ts diff --git a/repos/effect/packages/effect/src/cluster/SocketRunner.ts b/packages/effect/src/cluster/SocketRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SocketRunner.ts rename to packages/effect/src/cluster/SocketRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/SqlMessageStorage.ts b/packages/effect/src/cluster/SqlMessageStorage.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SqlMessageStorage.ts rename to packages/effect/src/cluster/SqlMessageStorage.ts diff --git a/repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts b/packages/effect/src/cluster/SqlRunnerStorage.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts rename to packages/effect/src/cluster/SqlRunnerStorage.ts diff --git a/repos/effect/packages/effect/src/cluster/TestRunner.ts b/packages/effect/src/cluster/TestRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/TestRunner.ts rename to packages/effect/src/cluster/TestRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/index.ts b/packages/effect/src/cluster/index.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/index.ts rename to packages/effect/src/cluster/index.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts b/packages/effect/src/cluster/internal/clusterAbandon.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts rename to packages/effect/src/cluster/internal/clusterAbandon.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/entityActivation.ts b/packages/effect/src/cluster/internal/entityActivation.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/entityActivation.ts rename to packages/effect/src/cluster/internal/entityActivation.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/entityManager.ts b/packages/effect/src/cluster/internal/entityManager.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/entityManager.ts rename to packages/effect/src/cluster/internal/entityManager.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/entityReaper.ts b/packages/effect/src/cluster/internal/entityReaper.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/entityReaper.ts rename to packages/effect/src/cluster/internal/entityReaper.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/hash.ts b/packages/effect/src/cluster/internal/hash.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/hash.ts rename to packages/effect/src/cluster/internal/hash.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/interruptors.ts b/packages/effect/src/cluster/internal/interruptors.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/interruptors.ts rename to packages/effect/src/cluster/internal/interruptors.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/resourceMap.ts b/packages/effect/src/cluster/internal/resourceMap.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/resourceMap.ts rename to packages/effect/src/cluster/internal/resourceMap.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/resourceRef.ts b/packages/effect/src/cluster/internal/resourceRef.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/resourceRef.ts rename to packages/effect/src/cluster/internal/resourceRef.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/shardLock.ts b/packages/effect/src/cluster/internal/shardLock.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/shardLock.ts rename to packages/effect/src/cluster/internal/shardLock.ts diff --git a/repos/effect/packages/effect/src/devtools/DevTools.ts b/packages/effect/src/devtools/DevTools.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevTools.ts rename to packages/effect/src/devtools/DevTools.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsClient.ts b/packages/effect/src/devtools/DevToolsClient.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsClient.ts rename to packages/effect/src/devtools/DevToolsClient.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsSchema.ts b/packages/effect/src/devtools/DevToolsSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsSchema.ts rename to packages/effect/src/devtools/DevToolsSchema.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsServer.ts b/packages/effect/src/devtools/DevToolsServer.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsServer.ts rename to packages/effect/src/devtools/DevToolsServer.ts diff --git a/repos/effect/packages/effect/src/devtools/index.ts b/packages/effect/src/devtools/index.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/index.ts rename to packages/effect/src/devtools/index.ts diff --git a/repos/effect/packages/effect/src/encoding/Base64.ts b/packages/effect/src/encoding/Base64.ts similarity index 98% rename from repos/effect/packages/effect/src/encoding/Base64.ts rename to packages/effect/src/encoding/Base64.ts index b04b5be45..012d4cb93 100644 --- a/repos/effect/packages/effect/src/encoding/Base64.ts +++ b/packages/effect/src/encoding/Base64.ts @@ -1,6 +1,7 @@ /** * Base64 encoding and decoding helpers. * + * @stability stable * @since 4.0.0 */ import * as Result from "../Result.ts" @@ -34,6 +35,7 @@ import { EncodingError } from "./EncodingError.ts" * @see {@link decode} for decoding standard Base64 to bytes * @see {@link decodeString} for decoding standard Base64 to UTF-8 text * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -62,6 +64,7 @@ export const encode: (input: Uint8Array | string) => string = (input) => * Base64.decode("SGVsbG8=") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -138,6 +141,7 @@ export const decode = (str: string): Result.Result => * Base64.decodeString("aGVsbG8=") // => Result.succeed("hello") * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/encoding/Base64Url.ts b/packages/effect/src/encoding/Base64Url.ts similarity index 97% rename from repos/effect/packages/effect/src/encoding/Base64Url.ts rename to packages/effect/src/encoding/Base64Url.ts index 01fb7d4d3..92486cb5f 100644 --- a/repos/effect/packages/effect/src/encoding/Base64Url.ts +++ b/packages/effect/src/encoding/Base64Url.ts @@ -1,6 +1,7 @@ /** * URL-safe Base64 encoding and decoding helpers. * + * @stability stable * @since 4.0.0 */ import * as Result from "../Result.ts" @@ -34,6 +35,7 @@ import { EncodingError } from "./EncodingError.ts" * @see {@link decode} for decoding URL-safe Base64 to bytes * @see {@link decodeString} for decoding URL-safe Base64 to UTF-8 text * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -62,6 +64,7 @@ export const encode: (input: Uint8Array | string) => string = (input) => * Base64Url.decode("SGVsbG8_") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111, 63])) * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -116,6 +119,7 @@ export const decode = (str: string): Result.Result => * Base64Url.decodeString("aGVsbG8_") // => Result.succeed("hello?") * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/encoding/EncodingError.ts b/packages/effect/src/encoding/EncodingError.ts similarity index 96% rename from repos/effect/packages/effect/src/encoding/EncodingError.ts rename to packages/effect/src/encoding/EncodingError.ts index c3ae39fff..c70a26d54 100644 --- a/repos/effect/packages/effect/src/encoding/EncodingError.ts +++ b/packages/effect/src/encoding/EncodingError.ts @@ -1,6 +1,7 @@ /** * Shared error type for encoding and decoding operations. * + * @stability stable * @since 4.0.0 */ import * as Data from "../Data.ts" @@ -22,6 +23,7 @@ import { hasProperty } from "../Predicate.ts" * * @see {@link isEncodingError} for the public guard that checks this marker * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -34,6 +36,7 @@ export const EncodingErrorTypeId = "~effect/encoding/EncodingError" as const * * Use to type the marker carried by `EncodingError` values. * + * @stability stable * @category type IDs * @since 4.0.0 */ @@ -55,6 +58,7 @@ export type EncodingErrorTypeId = typeof EncodingErrorTypeId * * @see {@link isEncodingError} for checking whether a value is an EncodingError * + * @stability stable * @category errors * @since 4.0.0 */ @@ -92,6 +96,7 @@ export class EncodingError extends Data.TaggedError("EncodingError")<{ * @see {@link EncodingError} for the structured error produced by failed * encoding and decoding operations * + * @stability stable * @category guards * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/encoding/Hex.ts b/packages/effect/src/encoding/Hex.ts similarity index 98% rename from repos/effect/packages/effect/src/encoding/Hex.ts rename to packages/effect/src/encoding/Hex.ts index 1ac78464d..a0048d69b 100644 --- a/repos/effect/packages/effect/src/encoding/Hex.ts +++ b/packages/effect/src/encoding/Hex.ts @@ -1,6 +1,7 @@ /** * Hexadecimal encoding, decoding, and random value helpers. * + * @stability stable * @since 4.0.0 */ import * as Result from "../Result.ts" @@ -26,6 +27,7 @@ import { EncodingError } from "./EncodingError.ts" * Hex.encode(bytes) // => "48656c6c6f" * ``` * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const encode: (input: Uint8Array | string) => string = (input) => * security-sensitive values, use the `Crypto.Crypto` service's `randomBytes` * method and encode the result with {@link encode}. * + * @stability stable * @category encoding * @since 4.0.0 */ @@ -173,6 +176,7 @@ const random32 = (): string => { * Hex.decode("48656c6c6f") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ @@ -233,6 +237,7 @@ export const decode = (str: string): Result.Result => * Hex.decodeString("68656c6c6f") // => Result.succeed("hello") * ``` * + * @stability stable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/encoding/Ini.ts b/packages/effect/src/encoding/Ini.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Ini.ts rename to packages/effect/src/encoding/Ini.ts diff --git a/repos/effect/packages/effect/src/encoding/Ndjson.ts b/packages/effect/src/encoding/Ndjson.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Ndjson.ts rename to packages/effect/src/encoding/Ndjson.ts diff --git a/repos/effect/packages/effect/src/encoding/SchemaBinary.ts b/packages/effect/src/encoding/SchemaBinary.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/SchemaBinary.ts rename to packages/effect/src/encoding/SchemaBinary.ts diff --git a/repos/effect/packages/effect/src/encoding/Sse.ts b/packages/effect/src/encoding/Sse.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Sse.ts rename to packages/effect/src/encoding/Sse.ts diff --git a/repos/effect/packages/effect/src/encoding/Toml.ts b/packages/effect/src/encoding/Toml.ts similarity index 98% rename from repos/effect/packages/effect/src/encoding/Toml.ts rename to packages/effect/src/encoding/Toml.ts index eae5c924c..b95d711e1 100644 --- a/repos/effect/packages/effect/src/encoding/Toml.ts +++ b/packages/effect/src/encoding/Toml.ts @@ -283,7 +283,7 @@ class TomlParser { if (multiline) { this.expect("\"") this.expect("\"") - if (this.peek() === "\n") this.advance() + this.advance(this.input.startsWith("\r\n", this.index) ? 2 : this.peek() === "\n" ? 1 : 0) } let output = "" while (!this.done) { @@ -347,7 +347,7 @@ class TomlParser { if (multiline) { this.expect("'") this.expect("'") - if (this.peek() === "\n") this.advance() + this.advance(this.input.startsWith("\r\n", this.index) ? 2 : this.peek() === "\n" ? 1 : 0) } const start = this.index while (!this.done) { diff --git a/repos/effect/packages/effect/src/encoding/Yaml.ts b/packages/effect/src/encoding/Yaml.ts similarity index 97% rename from repos/effect/packages/effect/src/encoding/Yaml.ts rename to packages/effect/src/encoding/Yaml.ts index ac8c23ab9..e553d226b 100644 --- a/repos/effect/packages/effect/src/encoding/Yaml.ts +++ b/packages/effect/src/encoding/Yaml.ts @@ -523,6 +523,12 @@ class YamlParser { } this.index = end + let contentEnd = content.length + while (contentEnd > 0 && content[contentEnd - 1].length === 0) contentEnd-- + const trailingBreaks = Math.max(0, content.length - contentEnd - (end === this.lines.length ? 1 : 0)) + + (contentEnd > 0 && start + contentEnd < this.lines.length ? 1 : 0) + content.length = contentEnd + let output = "" if (style === "|") { output = content.join("\n") @@ -543,11 +549,9 @@ class YamlParser { previousMoreIndented = moreIndented } } - output += "\n".repeat(blankLines) } - if (chomp === "keep") return output.endsWith("\n") ? output : `${output}\n` - output = output.replace(/\n+$/, "") - return chomp === "strip" ? output : `${output}\n` + if (chomp === "keep") return output + "\n".repeat(trailingBreaks) + return chomp === "clip" && contentEnd > 0 && trailingBreaks > 0 ? `${output}\n` : output } private skipBlankLines(): number { diff --git a/repos/effect/packages/effect/src/encoding/index.ts b/packages/effect/src/encoding/index.ts similarity index 91% rename from repos/effect/packages/effect/src/encoding/index.ts rename to packages/effect/src/encoding/index.ts index 6b19d09e5..7ac4657b0 100644 --- a/repos/effect/packages/effect/src/encoding/index.ts +++ b/packages/effect/src/encoding/index.ts @@ -6,21 +6,25 @@ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability stable * @since 4.0.0 */ export * as Base64 from "./Base64.ts" /** + * @stability stable * @since 4.0.0 */ export * as Base64Url from "./Base64Url.ts" /** + * @stability stable * @since 4.0.0 */ export * as EncodingError from "./EncodingError.ts" /** + * @stability stable * @since 4.0.0 */ export * as Hex from "./Hex.ts" diff --git a/repos/effect/packages/effect/src/eventlog/Event.ts b/packages/effect/src/eventlog/Event.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/Event.ts rename to packages/effect/src/eventlog/Event.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventGroup.ts b/packages/effect/src/eventlog/EventGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventGroup.ts rename to packages/effect/src/eventlog/EventGroup.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventJournal.ts b/packages/effect/src/eventlog/EventJournal.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventJournal.ts rename to packages/effect/src/eventlog/EventJournal.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLog.ts b/packages/effect/src/eventlog/EventLog.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLog.ts rename to packages/effect/src/eventlog/EventLog.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts b/packages/effect/src/eventlog/EventLogEncryption.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts rename to packages/effect/src/eventlog/EventLogEncryption.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogMessage.ts b/packages/effect/src/eventlog/EventLogMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogMessage.ts rename to packages/effect/src/eventlog/EventLogMessage.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogRemote.ts b/packages/effect/src/eventlog/EventLogRemote.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogRemote.ts rename to packages/effect/src/eventlog/EventLogRemote.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServer.ts b/packages/effect/src/eventlog/EventLogServer.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServer.ts rename to packages/effect/src/eventlog/EventLogServer.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts b/packages/effect/src/eventlog/EventLogServerEncrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts rename to packages/effect/src/eventlog/EventLogServerEncrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServerUnencrypted.ts b/packages/effect/src/eventlog/EventLogServerUnencrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServerUnencrypted.ts rename to packages/effect/src/eventlog/EventLogServerUnencrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts b/packages/effect/src/eventlog/EventLogSessionAuth.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts rename to packages/effect/src/eventlog/EventLogSessionAuth.ts diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts b/packages/effect/src/eventlog/SqlEventJournal.ts similarity index 96% rename from repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts rename to packages/effect/src/eventlog/SqlEventJournal.ts index d434f4fd5..1c9c4437d 100644 --- a/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts +++ b/packages/effect/src/eventlog/SqlEventJournal.ts @@ -56,7 +56,7 @@ export const make = (options?: { pg: () => sql` CREATE TABLE IF NOT EXISTS ${entryTableSql} ( - id UUID PRIMARY KEY, + id BYTEA PRIMARY KEY, event TEXT NOT NULL, primary_key TEXT NOT NULL, payload BYTEA NOT NULL, @@ -95,8 +95,8 @@ export const make = (options?: { pg: () => sql` CREATE TABLE IF NOT EXISTS ${remotesTableSql} ( - remote_id UUID NOT NULL, - entry_id UUID NOT NULL, + remote_id BYTEA NOT NULL, + entry_id BYTEA NOT NULL, sequence INT NOT NULL, PRIMARY KEY (remote_id, entry_id) )`, @@ -343,12 +343,26 @@ const SqlUint8Array = Schema.Union([Schema.Uint8Array, ArrayBuffer]).pipe( const SqlEntryId = SqlUint8Array.pipe(Schema.decodeTo(EventJournal.EntryId)) +const SqlNatural = Schema.Union([ + Schema.Natural, + Schema.FiniteFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0)), + Schema.BigInt.pipe( + Schema.decodeTo( + Schema.Natural, + SchemaTransformation.transform({ + decode: Number, + encode: BigInt + }) + ) + ) +]) + const EntryRow = Schema.Struct({ id: SqlEntryId, event: Schema.String, primary_key: Schema.String, payload: SqlUint8Array, - timestamp: Schema.Int + timestamp: SqlNatural }) const EntryRowArray = Schema.Array(EntryRow) diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts b/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts rename to packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts b/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts similarity index 56% rename from repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts rename to packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts index c3034c901..35a59f9d7 100644 --- a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts +++ b/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts @@ -11,6 +11,7 @@ * @since 4.0.0 */ import * as Arr from "../Array.ts" +import * as Context from "../Context.ts" import * as Effect from "../Effect.ts" import * as Layer from "../Layer.ts" import * as PubSub from "../PubSub.ts" @@ -18,12 +19,22 @@ import * as RcMap from "../RcMap.ts" import * as Schema from "../Schema.ts" import * as SchemaTransformation from "../SchemaTransformation.ts" import type * as Scope from "../Scope.ts" +import * as Semaphore from "../Semaphore.ts" import * as SqlClient from "../sql/SqlClient.ts" import * as SqlError from "../sql/SqlError.ts" import * as Stream from "../Stream.ts" import { Entry, EntryId, makeRemoteIdUnsafe, RemoteEntry, type RemoteId } from "./EventJournal.ts" import * as EventLogServerUnencrypted from "./EventLogServerUnencrypted.ts" +let storageIdCounter = 0 +const withTracerDisabled = Effect.withTracerEnabled(false) + +type StorageOptions = { + readonly entryTablePrefix?: string + readonly remoteIdTable?: string + readonly insertBatchSize?: number +} + /** * Creates unencrypted event-log server `Storage` backed by SQL. * @@ -37,59 +48,54 @@ import * as EventLogServerUnencrypted from "./EventLogServerUnencrypted.ts" * @category constructors * @since 4.0.0 */ -export const makeStorage = (options?: { - readonly entryTablePrefix?: string - readonly remoteIdTable?: string - readonly insertBatchSize?: number -}): Effect.Effect< +export const makeStorage = Effect.fnUntraced(function*(options?: StorageOptions): Effect.fn.Return< EventLogServerUnencrypted.Storage["Service"], SqlError.SqlError, SqlClient.SqlClient | Scope.Scope -> => - Effect.gen(function*() { - const sql = (yield* SqlClient.SqlClient).withoutTransforms() - - const entriesTable = options?.entryTablePrefix ?? "effect_events" - const remoteIdTable = options?.remoteIdTable ?? "effect_remote_id" - const insertBatchSize = options?.insertBatchSize ?? 200 - const storesTable = `${entriesTable}_stores` - const sessionAuthBindingsTable = `${entriesTable}_session_auth_bindings` - - const remoteIdTableSql = sql(remoteIdTable) - const entriesTableSql = sql(entriesTable) - const storesTableSql = sql(storesTable) - const sessionAuthBindingsTableSql = sql(sessionAuthBindingsTable) - - yield* sql.onDialectOrElse({ - pg: () => - sql` +> { + const sql = (yield* SqlClient.SqlClient).withoutTransforms() + + const entriesTable = options?.entryTablePrefix ?? "effect_events" + const remoteIdTable = options?.remoteIdTable ?? "effect_remote_id" + const insertBatchSize = options?.insertBatchSize ?? 200 + const storesTable = `${entriesTable}_stores` + const sessionAuthBindingsTable = `${entriesTable}_session_auth_bindings` + + const remoteIdTableSql = sql(remoteIdTable) + const entriesTableSql = sql(entriesTable) + const storesTableSql = sql(storesTable) + const sessionAuthBindingsTableSql = sql(sessionAuthBindingsTable) + + yield* sql.onDialectOrElse({ + pg: () => + sql` CREATE TABLE IF NOT EXISTS ${remoteIdTableSql} ( singleton INT PRIMARY KEY, remote_id BYTEA NOT NULL )`, - mysql: () => - sql` + mysql: () => + sql` CREATE TABLE IF NOT EXISTS ${remoteIdTableSql} ( singleton INT PRIMARY KEY, remote_id BINARY(16) NOT NULL )`, - mssql: () => - sql` + mssql: () => + sql` CREATE TABLE IF NOT EXISTS ${remoteIdTableSql} ( singleton INT PRIMARY KEY, remote_id VARBINARY(16) NOT NULL )`, - orElse: () => - sql` + orElse: () => + sql` CREATE TABLE IF NOT EXISTS ${remoteIdTableSql} ( singleton INTEGER PRIMARY KEY, remote_id BLOB NOT NULL )` - }) + }) - yield* sql.onDialectOrElse({ - pg: () => - sql` + yield* sql.onDialectOrElse({ + pg: () => + sql` CREATE TABLE IF NOT EXISTS ${entriesTableSql} ( store_id TEXT NOT NULL, sequence BIGINT NOT NULL, @@ -100,8 +106,8 @@ export const makeStorage = (options?: { PRIMARY KEY (store_id, sequence), UNIQUE (store_id, entry_id) )`, - mysql: () => - sql` + mysql: () => + sql` CREATE TABLE IF NOT EXISTS ${entriesTableSql} ( store_id VARCHAR(191) NOT NULL, sequence BIGINT NOT NULL, @@ -112,8 +118,8 @@ export const makeStorage = (options?: { PRIMARY KEY (store_id, sequence), UNIQUE (store_id, entry_id) )`, - mssql: () => - sql` + mssql: () => + sql` CREATE TABLE IF NOT EXISTS ${entriesTableSql} ( store_id NVARCHAR(191) NOT NULL, sequence BIGINT NOT NULL, @@ -124,8 +130,8 @@ export const makeStorage = (options?: { PRIMARY KEY (store_id, sequence), UNIQUE (store_id, entry_id) )`, - orElse: () => - sql` + orElse: () => + sql` CREATE TABLE IF NOT EXISTS ${entriesTableSql} ( store_id TEXT NOT NULL, sequence INTEGER NOT NULL, @@ -136,116 +142,161 @@ export const makeStorage = (options?: { PRIMARY KEY (store_id, sequence), UNIQUE (store_id, entry_id) )` - }) + }) - yield* sql.onDialectOrElse({ - pg: () => - sql` + yield* sql.onDialectOrElse({ + pg: () => + sql` CREATE TABLE IF NOT EXISTS ${storesTableSql} ( store_id TEXT PRIMARY KEY, next_sequence BIGINT NOT NULL )`, - mysql: () => - sql` + mysql: () => + sql` CREATE TABLE IF NOT EXISTS ${storesTableSql} ( store_id VARCHAR(191) PRIMARY KEY, next_sequence BIGINT NOT NULL )`, - mssql: () => - sql` + mssql: () => + sql` CREATE TABLE IF NOT EXISTS ${storesTableSql} ( store_id NVARCHAR(191) PRIMARY KEY, next_sequence BIGINT NOT NULL )`, - orElse: () => - sql` + orElse: () => + sql` CREATE TABLE IF NOT EXISTS ${storesTableSql} ( store_id TEXT PRIMARY KEY, next_sequence INTEGER NOT NULL )` - }) + }) - yield* sql.onDialectOrElse({ - pg: () => - sql` + yield* sql.onDialectOrElse({ + pg: () => + sql` CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} ( public_key TEXT PRIMARY KEY, signing_public_key BYTEA NOT NULL )`, - mysql: () => - sql` + mysql: () => + sql` CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} ( public_key VARCHAR(191) PRIMARY KEY, signing_public_key BINARY(32) NOT NULL )`, - mssql: () => - sql` + mssql: () => + sql` CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} ( public_key NVARCHAR(191) PRIMARY KEY, signing_public_key VARBINARY(32) NOT NULL )`, - orElse: () => - sql` + orElse: () => + sql` CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} ( public_key TEXT PRIMARY KEY, signing_public_key BLOB NOT NULL )` - }) + }) - const selectRemoteId = sql<{ remote_id: Uint8Array }>` + const selectRemoteId = sql<{ remote_id: Uint8Array }>` SELECT remote_id FROM ${remoteIdTableSql} WHERE singleton = 1 ` - const remoteId = yield* selectRemoteId.pipe( - Effect.flatMap((rows) => { - const existing = rows[0] - if (existing !== undefined) { - return Effect.succeed(existing.remote_id as RemoteId) - } + const remoteId = yield* selectRemoteId.pipe( + Effect.flatMap((rows) => { + const existing = rows[0] + if (existing !== undefined) { + return Effect.succeed(existing.remote_id as RemoteId) + } - const created = makeRemoteIdUnsafe() - return sql` + const created = makeRemoteIdUnsafe() + return sql` INSERT INTO ${remoteIdTableSql} (singleton, remote_id) VALUES (1, ${created}) `.pipe( - Effect.catchIf( - isConstraintConflict, - () => Effect.void - ), - Effect.andThen(selectRemoteId), - Effect.map((rows) => rows[0]?.remote_id as RemoteId | undefined), - Effect.map((persisted) => persisted ?? created) - ) - }) - ) - - const pubsubs = yield* RcMap.make({ - lookup: (_storeId: string) => - Effect.acquireRelease( - PubSub.unbounded(), - PubSub.shutdown + Effect.catchIf( + isConstraintConflict, + () => Effect.void ), - idleTimeToLive: "5 minutes" + Effect.andThen(selectRemoteId), + Effect.map((rows) => rows[0]?.remote_id as RemoteId | undefined), + Effect.map((persisted) => persisted ?? created) + ) }) + ) - const ensureStore = (storeId: string) => - sql.onDialectOrElse({ - pg: () => - sql` + const pubsubs = yield* RcMap.make({ + lookup: (_storeId: string) => + Effect.acquireRelease( + PubSub.unbounded(), + PubSub.shutdown + ), + idleTimeToLive: "5 minutes" + }) + + const transactionSemaphore = yield* Semaphore.make(1) + type PendingWrites = Array]> + const pendingWrites = Context.Service( + `effect/eventlog/SqlEventLogServerUnencrypted/PendingWrites/${storageIdCounter++}` + ) + const publish = Effect.fnUntraced(function*([storeId, entries]: PendingWrites[number]) { + const pubsub = yield* RcMap.get(pubsubs, storeId) + return yield* PubSub.publishAll(pubsub, entries) + }, Effect.scoped) + // Merge nested buffers on success; publish only after the outer commit. + // Hold the outer permit through publication to prevent later commits from overtaking it. + const withTransaction = Effect.fnUntraced(function*( + effect: Effect.Effect + ): Effect.fn.Return> { + const parent = yield* Effect.serviceOption(pendingWrites) + // Allow body interruption, but mask commit through publication. + const transaction = Effect.uninterruptibleMask(Effect.fnUntraced(function*(restore) { + const writes: PendingWrites = [] + const result = yield* sql.withTransaction( + Effect.provideService(restore(effect), pendingWrites, writes) + ) + if (parent._tag === "Some") { + parent.value.push(...writes) + } else { + const stores = new Map>() + for (const [storeId, entries] of writes) { + const existing = stores.get(storeId) + if (existing === undefined) { + stores.set(storeId, [...entries]) + } else { + existing.push(...entries) + } + } + // Sibling buffers may merge out of sequence order. + for (const entries of stores.values()) { + entries.sort((a, b) => a.remoteSequence - b.remoteSequence) + } + yield* Effect.forEach(stores, publish, { discard: true }) + } + return result + })) + // Wait interruptibly; nested transactions reuse the outer permit. + return yield* parent._tag === "Some" ? transaction : transactionSemaphore.withPermit(transaction) + }) + + const ensureStore = (storeId: string) => + sql.onDialectOrElse({ + pg: () => + sql` INSERT INTO ${storesTableSql} (store_id, next_sequence) VALUES (${storeId}, 1) ON CONFLICT (store_id) DO NOTHING `, - mysql: () => - sql` + mysql: () => + sql` INSERT INTO ${storesTableSql} (store_id, next_sequence) VALUES (${storeId}, 1) ON DUPLICATE KEY UPDATE store_id = store_id `, - mssql: () => - sql` + mssql: () => + sql` MERGE ${storesTableSql} WITH (HOLDLOCK) AS target USING (SELECT ${storeId} AS store_id, 1 AS next_sequence) AS source ON target.store_id = source.store_id @@ -253,194 +304,193 @@ export const makeStorage = (options?: { INSERT (store_id, next_sequence) VALUES (source.store_id, source.next_sequence); `, - orElse: () => - sql` + orElse: () => + sql` INSERT INTO ${storesTableSql} (store_id, next_sequence) VALUES (${storeId}, 1) ON CONFLICT DO NOTHING ` - }) + }) - const lockStore = (storeId: string) => - sql.onDialectOrElse({ - pg: () => - sql` + const lockStore = (storeId: string) => + sql.onDialectOrElse({ + pg: () => + sql` SELECT next_sequence FROM ${storesTableSql} WHERE store_id = ${storeId} FOR UPDATE `, - mysql: () => - sql` + mysql: () => + sql` SELECT next_sequence FROM ${storesTableSql} WHERE store_id = ${storeId} FOR UPDATE `, - mssql: () => - sql` + mssql: () => + sql` SELECT next_sequence FROM ${storesTableSql} WITH (UPDLOCK, HOLDLOCK) WHERE store_id = ${storeId} `, - orElse: () => - sql` + orElse: () => + sql` UPDATE ${storesTableSql} SET next_sequence = next_sequence WHERE store_id = ${storeId} RETURNING next_sequence ` - }).pipe( - Effect.flatMap(decodeStoreSequence) - ) + }).pipe( + Effect.flatMap(decodeStoreSequence) + ) - const setNextSequence = (storeId: string, nextSequence: number) => - sql` + const setNextSequence = (storeId: string, nextSequence: number) => + sql` UPDATE ${storesTableSql} SET next_sequence = ${nextSequence} WHERE store_id = ${storeId} ` - const selectEntriesAfter = (storeId: string, startSequence: number) => - sql` + const selectEntriesAfter = (storeId: string, startSequence: number) => + sql` SELECT sequence, entry_id, event, primary_key, payload FROM ${entriesTableSql} WHERE store_id = ${storeId} AND sequence >= ${startSequence} ORDER BY sequence ASC `.pipe( - Effect.flatMap(decodeRemoteEntries) - ) + Effect.flatMap(decodeRemoteEntries) + ) - const getSessionAuthBinding = (publicKey: string) => - sql` + const getSessionAuthBinding = (publicKey: string) => + sql` SELECT public_key, signing_public_key FROM ${sessionAuthBindingsTableSql} WHERE public_key = ${publicKey} `.pipe( - Effect.flatMap(decodeSessionAuthBindings), - Effect.map((rows) => { - const row = rows[0] - return row === undefined ? undefined : row.signing_public_key as Uint8Array - }), - Effect.orDie - ) + Effect.flatMap(decodeSessionAuthBindings), + Effect.map((rows) => { + const row = rows[0] + return row === undefined ? undefined : row.signing_public_key as Uint8Array + }), + Effect.orDie + ) - return EventLogServerUnencrypted.Storage.of({ - getId: Effect.succeed(remoteId), - getOrCreateSessionAuthBinding: Effect.fnUntraced( - function*(publicKey, signingPublicKey) { - const existing = yield* getSessionAuthBinding(publicKey) - if (existing !== undefined) { - return existing - } - return yield* sql` + return EventLogServerUnencrypted.Storage.of({ + getId: Effect.succeed(remoteId), + getOrCreateSessionAuthBinding: Effect.fnUntraced( + function*(publicKey, signingPublicKey) { + const existing = yield* getSessionAuthBinding(publicKey) + if (existing !== undefined) { + return existing + } + return yield* sql` INSERT INTO ${sessionAuthBindingsTableSql} (public_key, signing_public_key) VALUES (${publicKey}, ${signingPublicKey}) `.pipe( - Effect.as(signingPublicKey) - ) - }, - sql.withTransaction, - Effect.orDie, - withTracerDisabled - ), - entriesAfter: (storeId, entry) => - sql` + Effect.as(signingPublicKey) + ) + }, + sql.withTransaction, + Effect.orDie, + withTracerDisabled + ), + entriesAfter: (storeId, entry) => + sql` SELECT sequence, entry_id, event, primary_key, payload FROM ${entriesTableSql} WHERE store_id = ${storeId} AND entry_id >= ${entry.id} ORDER BY sequence ASC `.pipe( - Effect.flatMap(decodeRemoteEntries), - Effect.map(Arr.map((r) => r.entry)), - Effect.orDie, - withTracerDisabled - ), - write: Effect.fnUntraced( - function*(storeId, entries) { - if (entries.length === 0) { - return [] - } + Effect.flatMap(decodeRemoteEntries), + Effect.map(Arr.map((r) => r.entry)), + Effect.orDie, + withTracerDisabled + ), + write: Effect.fnUntraced( + function*(storeId, entries) { + if (entries.length === 0) { + return [] + } - yield* ensureStore(storeId) - const currentNextSequence = yield* lockStore(storeId) - - const committed: Array = [] - let rowsForInsert: Array<{ - readonly store_id: string - readonly sequence: number - readonly entry_id: Uint8Array - readonly event: string - readonly primary_key: string - readonly payload: Uint8Array - }> = [] - - for (let index = 0; index < entries.length; index++) { - const entry = entries[index] - const remoteSequence = currentNextSequence + index - committed.push( - new RemoteEntry({ - remoteSequence, - entry - }, { disableChecks: true }) - ) - rowsForInsert.push({ - store_id: storeId, - sequence: remoteSequence, - entry_id: entry.id, - event: entry.event, - primary_key: entry.primaryKey, - payload: entry.payload - }) - if (rowsForInsert.length >= insertBatchSize) { - yield* sql`INSERT INTO ${entriesTableSql} ${sql.insert(rowsForInsert)}` - rowsForInsert = [] - } - } - if (rowsForInsert.length > 0) { + yield* ensureStore(storeId) + const currentNextSequence = yield* lockStore(storeId) + + const committed: Array = [] + let rowsForInsert: Array<{ + readonly store_id: string + readonly sequence: number + readonly entry_id: Uint8Array + readonly event: string + readonly primary_key: string + readonly payload: Uint8Array + }> = [] + + for (let index = 0; index < entries.length; index++) { + const entry = entries[index] + const remoteSequence = currentNextSequence + index + committed.push( + new RemoteEntry({ + remoteSequence, + entry + }, { disableChecks: true }) + ) + rowsForInsert.push({ + store_id: storeId, + sequence: remoteSequence, + entry_id: entry.id, + event: entry.event, + primary_key: entry.primaryKey, + payload: entry.payload + }) + if (rowsForInsert.length >= insertBatchSize) { yield* sql`INSERT INTO ${entriesTableSql} ${sql.insert(rowsForInsert)}` + rowsForInsert = [] } - const nextSequence = currentNextSequence + entries.length - yield* setNextSequence(storeId, nextSequence) - - const pubsub = yield* RcMap.get(pubsubs, storeId) - yield* PubSub.publishAll(pubsub, committed) - - return committed - }, - Effect.scoped, - sql.withTransaction, - withTracerDisabled, - Effect.orDie - ), - changes: Effect.fnUntraced( - function*({ storeId, startSequence, compactors }) { - const pubsub = yield* RcMap.get(pubsubs, storeId) - const subscription = yield* PubSub.subscribe(pubsub) - const backlog = yield* EventLogServerUnencrypted.compactBacklog({ - compactors, - remoteEntries: yield* selectEntriesAfter(storeId, startSequence) - }) - let watermark = backlog.length > 0 ? backlog[backlog.length - 1]!.remoteSequence : startSequence - 1 - - return Stream.fromArray(backlog).pipe( - Stream.concat( - Stream.fromSubscription(subscription).pipe( - Stream.filter((entry) => entry.remoteSequence > watermark) - ) + } + if (rowsForInsert.length > 0) { + yield* sql`INSERT INTO ${entriesTableSql} ${sql.insert(rowsForInsert)}` + } + const nextSequence = currentNextSequence + entries.length + yield* setNextSequence(storeId, nextSequence) + + const writes = yield* pendingWrites + writes.push([storeId, committed]) + + return committed + }, + withTransaction, + withTracerDisabled, + Effect.orDie + ), + changes: Effect.fnUntraced( + function*({ storeId, startSequence, compactors }) { + const pubsub = yield* RcMap.get(pubsubs, storeId) + const subscription = yield* PubSub.subscribe(pubsub) + const backlog = yield* EventLogServerUnencrypted.compactBacklog({ + compactors, + remoteEntries: yield* selectEntriesAfter(storeId, startSequence) + }) + let watermark = backlog.length > 0 ? backlog[backlog.length - 1]!.remoteSequence : startSequence - 1 + + return Stream.fromArray(backlog).pipe( + Stream.concat( + Stream.fromSubscription(subscription).pipe( + Stream.filter((entry) => entry.remoteSequence > watermark) ) ) - }, - Effect.orDie, - withTracerDisabled, - Stream.unwrap - ), - withTransaction: (effect) => - sql.withTransaction(effect).pipe( - Effect.catchIf(SqlError.isSqlError, Effect.die) ) - }) - }).pipe(withTracerDisabled) + }, + Effect.orDie, + withTracerDisabled, + Stream.unwrap + ), + withTransaction: (effect) => + withTransaction(effect).pipe( + Effect.catchIf(SqlError.isSqlError, Effect.die) + ) + }) +}, (effect, _options?: StorageOptions) => withTracerDisabled(effect)) /** * Provides unencrypted server `Storage` using the SQL-backed implementation. @@ -449,12 +499,11 @@ export const makeStorage = (options?: { * @category layers * @since 4.0.0 */ -export const layerStorage = (options?: { - readonly entryTablePrefix?: string - readonly remoteIdTable?: string - readonly insertBatchSize?: number -}): Layer.Layer => - Layer.effect(EventLogServerUnencrypted.Storage)(makeStorage(options)) +export const layerStorage = (options?: StorageOptions): Layer.Layer< + EventLogServerUnencrypted.Storage, + SqlError.SqlError, + SqlClient.SqlClient +> => Layer.effect(EventLogServerUnencrypted.Storage)(makeStorage(options)) const EntrySql = Schema.Struct({ entry_id: EntryId, @@ -537,7 +586,5 @@ const decodeSessionAuthBindings = ( rows: unknown ): Effect.Effect, Schema.SchemaError> => decodeSessionAuthBindingRows(rows) -const withTracerDisabled = Effect.withTracerEnabled(false) - const isConstraintConflict = (error: SqlError.SqlError): boolean => error.reason._tag === "ConstraintError" || error.reason._tag === "UniqueViolation" diff --git a/repos/effect/packages/effect/src/eventlog/index.ts b/packages/effect/src/eventlog/index.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/index.ts rename to packages/effect/src/eventlog/index.ts diff --git a/repos/effect/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts b/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts rename to packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApi.ts b/packages/effect/src/http-api/HttpApi.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApi.ts rename to packages/effect/src/http-api/HttpApi.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts b/packages/effect/src/http-api/HttpApiBuilder.ts similarity index 99% rename from repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts rename to packages/effect/src/http-api/HttpApiBuilder.ts index 0bad0d35c..21a033919 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts +++ b/packages/effect/src/http-api/HttpApiBuilder.ts @@ -746,7 +746,11 @@ function buildPayloadDecoders( ): Map { const result = new Map() payloadMap.forEach(({ encoding, schemas }, contentType) => { - const decode = Schema.decodeUnknownEffect(Schema.Union(schemas), options) + const schema = Schema.Union(schemas) + const decode = Schema.decodeUnknownEffect( + encoding._tag === "FormUrlEncoded" ? Schema.toCodecArrayFromSingle(schema) : schema, + options + ) if (encoding._tag === "Multipart") { result.set(contentType, { _tag: "Multipart", mode: encoding.mode, limits: encoding.limits, decode }) } else { diff --git a/repos/effect/packages/effect/src/http-api/HttpApiClient.ts b/packages/effect/src/http-api/HttpApiClient.ts similarity index 98% rename from repos/effect/packages/effect/src/http-api/HttpApiClient.ts rename to packages/effect/src/http-api/HttpApiClient.ts index abfed1134..6bf1a5bfc 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiClient.ts +++ b/packages/effect/src/http-api/HttpApiClient.ts @@ -72,7 +72,16 @@ export type ForApi = Api e HttpApi.HttpApi ? Client : never -type SuccessType = S extends HttpApiSchema.WithHeaders< +/** + * Computes the decoded success value a client receives for an endpoint + * success schema, including headers, streaming Server-Sent Events, and + * streaming byte responses. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ +export type SuccessType = S extends HttpApiSchema.WithHeaders< infer _Inner, infer _Headers > ? HttpApiSchema.withHeaders, _Headers["Type"]> @@ -126,9 +135,9 @@ export declare namespace Client { * @category models * @since 4.0.0 */ - export type Response = [Mode] extends ["decoded-and-response"] + export type Response = Mode extends "decoded-and-response" ? [Success, HttpClientResponse.HttpClientResponse] - : [Mode] extends ["response-only"] ? HttpClientResponse.HttpClientResponse + : Mode extends "response-only" ? HttpClientResponse.HttpClientResponse : Success type GroupByEndpoint = Group["endpoints"] extends @@ -184,7 +193,7 @@ export declare namespace Client { Endpoint extends HttpApiEndpoint.ConstraintRequest, E, R - > = ( + > = ( request: Simplify< HttpApiEndpoint.ClientRequest< Endpoint["~Params"], diff --git a/repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts b/packages/effect/src/http-api/HttpApiEndpoint.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts rename to packages/effect/src/http-api/HttpApiEndpoint.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiError.ts b/packages/effect/src/http-api/HttpApiError.ts similarity index 97% rename from repos/effect/packages/effect/src/http-api/HttpApiError.ts rename to packages/effect/src/http-api/HttpApiError.ts index 87660b57b..cae629e79 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiError.ts +++ b/packages/effect/src/http-api/HttpApiError.ts @@ -467,8 +467,9 @@ export const HttpApiSchemaErrorTypeId: HttpApiSchemaErrorTypeId = "~effect/http- /** * Error raised when an HTTP API request or response component fails schema - * decoding or encoding. It records which component failed and responds as an - * empty `400 Bad Request` when rendered as a server response. + * decoding or encoding. Request decoding failures respond with an empty + * `400 Bad Request` and are ignored by error reporters. Response encoding + * failures respond with an empty `500 Internal Server Error` and are reported. * * @stability unstable * @category errors @@ -479,7 +480,10 @@ export class HttpApiSchemaError extends Data.TaggedClass("HttpApiSchemaError")<{ readonly cause: Schema.SchemaError }> { readonly [HttpApiSchemaErrorTypeId]: HttpApiSchemaErrorTypeId = HttpApiSchemaErrorTypeId - readonly [ErrorReporter.ignore] = true + + get [ErrorReporter.ignore](): boolean { + return this.kind !== "Body" && this.kind !== "ResponseHeaders" + } static is(u: unknown): u is HttpApiSchemaError { return hasProperty(u, HttpApiSchemaErrorTypeId) @@ -498,6 +502,6 @@ export class HttpApiSchemaError extends Data.TaggedClass("HttpApiSchemaError")<{ readonly message = this.kind; [HttpServerRespondable.symbol]() { - return Effect.succeed(badRequestResponse) + return Effect.succeed(this[ErrorReporter.ignore] ? badRequestResponse : internalServerErrorResponse) } } diff --git a/repos/effect/packages/effect/src/http-api/HttpApiGroup.ts b/packages/effect/src/http-api/HttpApiGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiGroup.ts rename to packages/effect/src/http-api/HttpApiGroup.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts b/packages/effect/src/http-api/HttpApiMiddleware.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts rename to packages/effect/src/http-api/HttpApiMiddleware.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiScalar.ts b/packages/effect/src/http-api/HttpApiScalar.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiScalar.ts rename to packages/effect/src/http-api/HttpApiScalar.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSchema.ts b/packages/effect/src/http-api/HttpApiSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiSchema.ts rename to packages/effect/src/http-api/HttpApiSchema.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts b/packages/effect/src/http-api/HttpApiSecurity.ts similarity index 93% rename from repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts rename to packages/effect/src/http-api/HttpApiSecurity.ts index 37a223182..98c8f61ec 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts +++ b/packages/effect/src/http-api/HttpApiSecurity.ts @@ -126,8 +126,8 @@ const Proto = { * * **Details** * - * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this - * security scheme. + * Use `HttpApiMiddleware.Service` with a `security` option to implement API + * middleware for this security scheme. * * @see {@link apiKey} for an API-key security scheme * @see {@link basic} for an HTTP Basic security scheme @@ -155,8 +155,8 @@ export const http = (options: { * * **Details** * - * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this - * security scheme. + * Use `HttpApiMiddleware.Service` with a `security` option to implement API + * middleware for this security scheme. * * @see {@link apiKey} for an API-key security scheme * @see {@link basic} for an HTTP Basic security scheme @@ -176,8 +176,8 @@ export const bearer: Http = http({ scheme: "Bearer" }) * * **Details** * - * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this - * security scheme. + * Use `HttpApiMiddleware.Service` with a `security` option to implement API + * middleware for this security scheme. * * Use `HttpApiBuilder.securitySetCookie` to set the correct cookie in a * handler. By default, `in` is `"header"`. @@ -208,8 +208,8 @@ export const apiKey = (options: { * * **Details** * - * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this - * security scheme. + * Use `HttpApiMiddleware.Service` with a `security` option to implement API + * middleware for this security scheme. * * @see {@link bearer} for a Bearer token security scheme * @see {@link apiKey} for an API-key security scheme diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts b/packages/effect/src/http-api/HttpApiSwagger.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts rename to packages/effect/src/http-api/HttpApiSwagger.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiTest.ts b/packages/effect/src/http-api/HttpApiTest.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiTest.ts rename to packages/effect/src/http-api/HttpApiTest.ts diff --git a/repos/effect/packages/effect/src/http-api/OpenApi.ts b/packages/effect/src/http-api/OpenApi.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/OpenApi.ts rename to packages/effect/src/http-api/OpenApi.ts diff --git a/repos/effect/packages/effect/src/http-api/index.ts b/packages/effect/src/http-api/index.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/index.ts rename to packages/effect/src/http-api/index.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/html.ts b/packages/effect/src/http-api/internal/html.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/html.ts rename to packages/effect/src/http-api/internal/html.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts b/packages/effect/src/http-api/internal/httpApiScalar.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts rename to packages/effect/src/http-api/internal/httpApiScalar.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/httpApiSwagger.ts b/packages/effect/src/http-api/internal/httpApiSwagger.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/httpApiSwagger.ts rename to packages/effect/src/http-api/internal/httpApiSwagger.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/mediaType.ts b/packages/effect/src/http-api/internal/mediaType.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/mediaType.ts rename to packages/effect/src/http-api/internal/mediaType.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/path.ts b/packages/effect/src/http-api/internal/path.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/path.ts rename to packages/effect/src/http-api/internal/path.ts diff --git a/repos/effect/packages/effect/src/http/Cookies.ts b/packages/effect/src/http/Cookies.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Cookies.ts rename to packages/effect/src/http/Cookies.ts diff --git a/repos/effect/packages/effect/src/http/Etag.ts b/packages/effect/src/http/Etag.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Etag.ts rename to packages/effect/src/http/Etag.ts diff --git a/repos/effect/packages/effect/src/http/FetchHttpClient.ts b/packages/effect/src/http/FetchHttpClient.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FetchHttpClient.ts rename to packages/effect/src/http/FetchHttpClient.ts diff --git a/repos/effect/packages/effect/src/http/FindMyWay.ts b/packages/effect/src/http/FindMyWay.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FindMyWay.ts rename to packages/effect/src/http/FindMyWay.ts diff --git a/repos/effect/packages/effect/src/http/FindMyWay/internal/queryString.ts b/packages/effect/src/http/FindMyWay/internal/queryString.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FindMyWay/internal/queryString.ts rename to packages/effect/src/http/FindMyWay/internal/queryString.ts diff --git a/repos/effect/packages/effect/src/http/FindMyWay/internal/router.ts b/packages/effect/src/http/FindMyWay/internal/router.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FindMyWay/internal/router.ts rename to packages/effect/src/http/FindMyWay/internal/router.ts diff --git a/repos/effect/packages/effect/src/http/Headers.ts b/packages/effect/src/http/Headers.ts similarity index 97% rename from repos/effect/packages/effect/src/http/Headers.ts rename to packages/effect/src/http/Headers.ts index a7c176cd4..0ec53bbcd 100644 --- a/repos/effect/packages/effect/src/http/Headers.ts +++ b/packages/effect/src/http/Headers.ts @@ -424,7 +424,9 @@ export const isRedactedName = ( * * **Details** * - * Defaults include `authorization`, `cookie`, `set-cookie`, and `x-api-key`. + * Defaults include `authorization`, `cookie`, `set-cookie`, `x-api-key` and + * `AWSAccessKeyId`, `Signature`, `sig` and `X-Goog-Signature`. Names match + * case-insensitively, and providing a new list replaces the defaults. * * @stability unstable * @category services @@ -437,6 +439,10 @@ export const CurrentRedactedNames = Context.Reference< "authorization", "cookie", "set-cookie", - "x-api-key" + "x-api-key", + "AWSAccessKeyId", + "Signature", + "sig", + "X-Goog-Signature" ] }) diff --git a/repos/effect/packages/effect/src/http/HttpBody.ts b/packages/effect/src/http/HttpBody.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpBody.ts rename to packages/effect/src/http/HttpBody.ts diff --git a/repos/effect/packages/effect/src/http/HttpClient.ts b/packages/effect/src/http/HttpClient.ts similarity index 96% rename from repos/effect/packages/effect/src/http/HttpClient.ts rename to packages/effect/src/http/HttpClient.ts index 184bfb0ba..814dd8113 100644 --- a/repos/effect/packages/effect/src/http/HttpClient.ts +++ b/packages/effect/src/http/HttpClient.ts @@ -18,15 +18,18 @@ import { Clock } from "../Clock.ts" import * as Context from "../Context.ts" import * as Duration from "../Duration.ts" import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" import * as Fiber from "../Fiber.ts" import { constant, constFalse, constTrue, dual, flow, identity } from "../Function.ts" import * as Inspectable from "../Inspectable.ts" +import { getStackTraceLimit, setStackTraceLimit } from "../internal/stackTraceLimit.ts" import * as Layer from "../Layer.ts" import * as Option from "../Option.ts" import type * as RateLimiter from "../persistence/RateLimiter.ts" import { type Pipeable, pipeArguments } from "../Pipeable.ts" import * as Predicate from "../Predicate.ts" import * as Ref from "../Ref.ts" +import * as References from "../References.ts" import * as Result from "../Result.ts" import * as Schedule from "../Schedule.ts" import type * as Scope from "../Scope.ts" @@ -42,6 +45,7 @@ import * as HttpClientResponse from "./HttpClientResponse.ts" import * as HttpIncomingMessage from "./HttpIncomingMessage.ts" import * as HttpMethod from "./HttpMethod.ts" import * as TraceContext from "./HttpTraceContext.ts" +import * as tracing from "./internal/tracing.ts" import * as Url from "./Url.ts" const TypeId = "~effect/http/HttpClient" @@ -722,26 +726,10 @@ export const make = ( { kind: "client" }, (span) => { span.attribute("http.request.method", request.method) - span.attribute("server.address", url.origin) - if (url.port !== "") { - span.attribute("server.port", +url.port) - } - span.attribute("url.full", url.toString()) - span.attribute("url.path", url.pathname) - span.attribute("url.scheme", url.protocol.slice(0, -1)) - const query = url.search.slice(1) - if (query !== "") { - span.attribute("url.query", query) - } const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames) + tracing.addUrlAttributes(span, url) const headerFilter = fiber.getRef(TracerHeaderFilter) - for (const name in request.headers) { - if (!headerFilter(name, "request")) continue - span.attribute( - `http.request.header.${name}`, - Headers.isRedactedName(name, redactedHeaderNames) ? "" : request.headers[name] - ) - } + tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames) request = fiber.getRef(TracerPropagationEnabled) ? HttpClientRequest.setHeaders(request, TraceContext.toHeaders(span)) : request @@ -751,12 +739,12 @@ export const make = ( Effect.matchCauseEffect({ onSuccess: (response) => { span.attribute("http.response.status_code", response.status) - for (const name in response.headers) { - if (!headerFilter(name, "response")) continue - span.attribute( - `http.response.header.${name}`, - Headers.isRedactedName(name, redactedHeaderNames) ? "" : response.headers[name] - ) + tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames) + // OpenTelemetry marks client spans as failed for 4xx and + // 5xx responses, while the response itself still succeeds. + if (response.status >= 400 && response.status < 600) { + span.attribute("error.type", String(response.status)) + endSpanWithStatusError(fiber, span, request, response) } if (scopedController) return Effect.succeed(response) @@ -776,6 +764,26 @@ export const make = ( ) })), Effect.succeed as HttpClient.Preprocess) +const endSpanWithStatusError = ( + fiber: Fiber.Fiber, + span: Tracer.Span, + request: HttpClientRequest.HttpClientRequest, + response: HttpClientResponse.HttpClientResponse +): void => { + const stackTraceLimit = getStackTraceLimit() + setStackTraceLimit(0) + let exit: Exit.Exit + try { + exit = Exit.fail(new Error.HttpClientError({ reason: new Error.StatusCodeError({ request, response }) })) + } finally { + setStackTraceLimit(stackTraceLimit) + } + span.end( + fiber.getRef(References.TracerTimingEnabled) ? fiber.getRef(Clock).currentTimeNanosUnsafe() : BigInt(0), + exit + ) +} + /** * Appends a transformation of the request object before sending it. * @@ -1669,6 +1677,11 @@ export const TracerDisabledWhen = Context.Reference< /** * Context reference for filtering request and response headers added to client spans. * + * **Details** + * + * Header capture is opt-in: the default filter records no headers. Captured + * headers listed in `Headers.CurrentRedactedNames` are recorded as ``. + * * @stability unstable * @category services * @since 4.0.0 @@ -1676,7 +1689,7 @@ export const TracerDisabledWhen = Context.Reference< export const TracerHeaderFilter = Context.Reference< (headerName: string, phase: "request" | "response") => boolean >("effect/http/HttpClient/TracerHeaderFilter", { - defaultValue: () => constTrue + defaultValue: () => constFalse }) /** @@ -1693,6 +1706,10 @@ export const TracerPropagationEnabled = Context.Reference("effect/http/ /** * Context reference for generating the span name used for outgoing client request spans. * + * **Details** + * + * Defaults to the request method unchanged. + * * @stability unstable * @category services * @since 4.0.0 @@ -1700,7 +1717,7 @@ export const TracerPropagationEnabled = Context.Reference("effect/http/ export const SpanNameGenerator = Context.Reference< (request: HttpClientRequest.HttpClientRequest) => string >("effect/http/HttpClient/SpanNameGenerator", { - defaultValue: () => (request) => `http.client ${request.method}` + defaultValue: () => (request) => request.method }) /** @@ -1742,7 +1759,7 @@ const responseRegistry = (() => { } } - const timers = new Map() + const timers = new WeakMap() return { register(response: HttpClientResponse.HttpClientResponse, controller: AbortController) { timers.set(response, setTimeout(() => controller.abort(), 5000)) diff --git a/repos/effect/packages/effect/src/http/HttpClientError.ts b/packages/effect/src/http/HttpClientError.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpClientError.ts rename to packages/effect/src/http/HttpClientError.ts diff --git a/repos/effect/packages/effect/src/http/HttpClientRequest.ts b/packages/effect/src/http/HttpClientRequest.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpClientRequest.ts rename to packages/effect/src/http/HttpClientRequest.ts diff --git a/repos/effect/packages/effect/src/http/HttpClientResponse.ts b/packages/effect/src/http/HttpClientResponse.ts similarity index 98% rename from repos/effect/packages/effect/src/http/HttpClientResponse.ts rename to packages/effect/src/http/HttpClientResponse.ts index 1e0769916..d8845b536 100644 --- a/repos/effect/packages/effect/src/http/HttpClientResponse.ts +++ b/packages/effect/src/http/HttpClientResponse.ts @@ -315,7 +315,9 @@ class WebHttpClientResponse extends Inspectable.Class implements HttpClientRespo if (this.cachedCookies) { return this.cachedCookies } - return this.cachedCookies = Cookies.fromSetCookie(this.source.headers.getSetCookie()) + // React Native's Headers does not implement getSetCookie, and it never + // exposes Set-Cookie to JavaScript anyway. + return this.cachedCookies = Cookies.fromSetCookie(this.source.headers.getSetCookie?.() ?? []) } get remoteAddress(): Option.Option { diff --git a/repos/effect/packages/effect/src/http/HttpEffect.ts b/packages/effect/src/http/HttpEffect.ts similarity index 97% rename from repos/effect/packages/effect/src/http/HttpEffect.ts rename to packages/effect/src/http/HttpEffect.ts index 7841d767e..16e0fb4e2 100644 --- a/repos/effect/packages/effect/src/http/HttpEffect.ts +++ b/packages/effect/src/http/HttpEffect.ts @@ -33,6 +33,9 @@ import * as preResponseHandler from "./internal/preResponseHandler.ts" /** * Runs an HTTP server effect, sends the produced response with the supplied handler, and converts failures into HTTP responses. * On handler failure, the request scope closes with the original cause. + * If the send handler returns an `HttpServerResponse`, such as the `101` + * response of an upgraded request, middleware observes it in place of the + * handler's response. * * @stability unstable * @category combinators @@ -81,11 +84,17 @@ export const toHandled = ( const handler = preResponseHandler.requestPreResponseHandlers.get(request.source) if (handler === undefined) { ;(request as any)[handledSymbol] = true - return Effect.mapEager(handleResponse(request, response), () => response) + return Effect.mapEager( + handleResponse(request, response), + (sent) => Response.isHttpServerResponse(sent) ? sent : response + ) } return Effect.flatMapEager(handler(request, response), (sentResponse) => { ;(request as any)[handledSymbol] = true - return Effect.mapEager(handleResponse(request, sentResponse), () => response) + return Effect.mapEager( + handleResponse(request, sentResponse), + (sent) => Response.isHttpServerResponse(sent) ? sent : response + ) }) } diff --git a/repos/effect/packages/effect/src/http/HttpIncomingMessage.ts b/packages/effect/src/http/HttpIncomingMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpIncomingMessage.ts rename to packages/effect/src/http/HttpIncomingMessage.ts diff --git a/repos/effect/packages/effect/src/http/HttpMethod.ts b/packages/effect/src/http/HttpMethod.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpMethod.ts rename to packages/effect/src/http/HttpMethod.ts diff --git a/repos/effect/packages/effect/src/http/HttpMiddleware.ts b/packages/effect/src/http/HttpMiddleware.ts similarity index 88% rename from repos/effect/packages/effect/src/http/HttpMiddleware.ts rename to packages/effect/src/http/HttpMiddleware.ts index 4c53b9b09..0b818c4e1 100644 --- a/repos/effect/packages/effect/src/http/HttpMiddleware.ts +++ b/packages/effect/src/http/HttpMiddleware.ts @@ -11,6 +11,7 @@ * @stability unstable * @since 4.0.0 */ +import * as Cause from "../Cause.ts" import { Clock } from "../Clock.ts" import * as Context from "../Context.ts" import * as Effect from "../Effect.ts" @@ -18,6 +19,7 @@ import * as Exit from "../Exit.ts" import type * as Fiber from "../Fiber.ts" import { constant, constFalse } from "../Function.ts" import * as internalEffect from "../internal/effect.ts" +import { getStackTraceLimit, setStackTraceLimit } from "../internal/stackTraceLimit.ts" import * as Layer from "../Layer.ts" import * as Option from "../Option.ts" import type { Predicate } from "../Predicate.ts" @@ -26,7 +28,7 @@ import { nativeTracer, ParentSpan, Tracer } from "../Tracer.ts" import * as Headers from "./Headers.ts" import type { CompressionAlgorithm } from "./HttpPlatform.ts" import { HttpPlatform } from "./HttpPlatform.ts" -import { causeResponseStripped, ClientAbort } from "./HttpServerError.ts" +import { causeResponseStripped, ClientAbort, HttpServerError, ResponseError } from "./HttpServerError.ts" import { HttpServerRequest } from "./HttpServerRequest.ts" import * as Request from "./HttpServerRequest.ts" import * as Response from "./HttpServerResponse.ts" @@ -35,6 +37,7 @@ import * as TraceContext from "./HttpTraceContext.ts" import * as compressionInternal from "./internal/compression.ts" import * as bodyInternal from "./internal/httpBody.ts" import { appendPreResponseHandlerUnsafe } from "./internal/preResponseHandler.ts" +import * as tracing from "./internal/tracing.ts" /** * Middleware that transforms an HTTP server app effect into another HTTP server app effect. @@ -130,15 +133,37 @@ export const layerTracerDisabledForUrls = ( /** * Context reference for generating server span names from HTTP server requests. * + * **Details** + * + * Defaults to the request method unchanged. + * * @stability unstable * @category services * @since 4.0.0 */ export const SpanNameGenerator = Context.Reference<(request: HttpServerRequest) => string>( "@effect/platform/HttpMiddleware/SpanNameGenerator", - { defaultValue: () => (request) => `http.server ${request.method}` } + { defaultValue: () => (request) => request.method } ) +/** + * Context reference for filtering request and response headers added to server spans. + * + * **Details** + * + * Header capture is opt-in: the default filter records no headers. Captured + * headers listed in `Headers.CurrentRedactedNames` are recorded as ``. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const TracerHeaderFilter = Context.Reference< + (headerName: string, phase: "request" | "response") => boolean +>("effect/http/HttpMiddleware/TracerHeaderFilter", { + defaultValue: () => constFalse +}) + /** * Middleware that logs sent HTTP responses with request method, request URL, and response status annotations. * @@ -195,6 +220,23 @@ export const isTracerDisabledUnsafe = ( request: HttpServerRequest ): boolean => !fiber.cache.tracerEnabled || fiber.getRef(TracerDisabledWhen)(request) +// OpenTelemetry requires failed server spans for 5xx responses. +const responseSpanExit = ( + request: HttpServerRequest, + response: HttpServerResponse +): Exit.Exit => { + if (!(response.status >= 500 && response.status < 600)) { + return Exit.succeed(response) + } + const stackTraceLimit = getStackTraceLimit() + setStackTraceLimit(0) + try { + return Exit.fail(new HttpServerError({ reason: new ResponseError({ request, response }) })) + } finally { + setStackTraceLimit(stackTraceLimit) + } +} + /** * Middleware that creates a server trace span for each request and records request and response HTTP attributes. * @@ -221,71 +263,44 @@ export const tracer: ( fiber.setContext(prevServices) const endTime = fiber.getRef(Clock).currentTimeNanosUnsafe() if (Exit.isSuccess(exit) && (!span.sampled || fiber.getRef(Tracer) === nativeTracer)) { - span.end(endTime, exit) + span.end(endTime, responseSpanExit(request, exit.value)) return undefined } const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames) + const headerFilter = fiber.getRef(TracerHeaderFilter) fiber.currentDispatcher.scheduleTask(() => { let response: HttpServerResponse - let spanExit = exit + let spanExit: Exit.Exit if (Exit.isFailure(exit)) { const [failureResponse, cause] = causeResponseStripped(exit.cause) response = failureResponse - spanExit = Option.isSome(cause) ? Exit.failCause(cause.value) : Exit.succeed(response) + // OpenTelemetry only treats 5xx responses as server span errors; interrupts still fail the span. + spanExit = Option.isSome(cause) && (response.status >= 500 || Cause.hasInterrupts(cause.value)) + ? Exit.failCause(cause.value) + : responseSpanExit(request, response) } else { response = exit.value + spanExit = responseSpanExit(request, response) } if (span.sampled) { span.attribute("http.request.method", request.method) - if (request.url.startsWith("/")) { - const host = request.headers.host ?? "localhost" - const protocol = request.headers["x-forwarded-proto"] === "https" ? "https" : "http" - span.attribute("url.full", `${protocol}://${host}${request.url}`) - const queryIndex = request.url.indexOf("?") - if (queryIndex === -1) { - span.attribute("url.path", request.url) - } else { - span.attribute("url.path", request.url.slice(0, queryIndex)) - if (queryIndex < request.url.length - 1) { - span.attribute("url.query", request.url.slice(queryIndex + 1)) - } - } - span.attribute("url.scheme", protocol) - } else { - const url = Request.toURL(request) - if (Option.isSome(url)) { - if (url.value.username !== "" || url.value.password !== "") { - url.value.username = "REDACTED" - url.value.password = "REDACTED" - } - span.attribute("url.full", url.value.toString()) - span.attribute("url.path", url.value.pathname) - const query = url.value.search.slice(1) - if (query !== "") { - span.attribute("url.query", query) - } - span.attribute("url.scheme", url.value.protocol.slice(0, -1)) + const url = Request.toURL(request) + if (Option.isSome(url)) { + if (!request.url.startsWith("/") && (url.value.username !== "" || url.value.password !== "")) { + url.value.username = "REDACTED" + url.value.password = "REDACTED" } + tracing.addUrlAttributes(span, url.value) } if (request.headers["user-agent"] !== undefined) { span.attribute("user_agent.original", request.headers["user-agent"]) } - for (const name in request.headers) { - span.attribute( - `http.request.header.${name}`, - Headers.isRedactedName(name, redactedHeaderNames) ? "" : request.headers[name] - ) - } + tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames) if (Option.isSome(request.remoteAddress)) { span.attribute("client.address", request.remoteAddress.value) } span.attribute("http.response.status_code", response.status) - for (const name in response.headers) { - span.attribute( - `http.response.header.${name}`, - Headers.isRedactedName(name, redactedHeaderNames) ? "" : response.headers[name] - ) - } + tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames) } span.end(endTime, spanExit) }, 0) diff --git a/repos/effect/packages/effect/src/http/HttpPlatform.ts b/packages/effect/src/http/HttpPlatform.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpPlatform.ts rename to packages/effect/src/http/HttpPlatform.ts diff --git a/repos/effect/packages/effect/src/http/HttpRouter.ts b/packages/effect/src/http/HttpRouter.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpRouter.ts rename to packages/effect/src/http/HttpRouter.ts diff --git a/repos/effect/packages/effect/src/http/HttpServer.ts b/packages/effect/src/http/HttpServer.ts similarity index 99% rename from repos/effect/packages/effect/src/http/HttpServer.ts rename to packages/effect/src/http/HttpServer.ts index 18eca5ba8..ef698bc08 100644 --- a/repos/effect/packages/effect/src/http/HttpServer.ts +++ b/packages/effect/src/http/HttpServer.ts @@ -51,7 +51,7 @@ export class HttpServer extends Context.Service | Scope.Scope + Exclude, HttpServerRequest> | Scope.Scope > } diff --git a/repos/effect/packages/effect/src/http/HttpServerError.ts b/packages/effect/src/http/HttpServerError.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServerError.ts rename to packages/effect/src/http/HttpServerError.ts diff --git a/repos/effect/packages/effect/src/http/HttpServerRequest.ts b/packages/effect/src/http/HttpServerRequest.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServerRequest.ts rename to packages/effect/src/http/HttpServerRequest.ts diff --git a/repos/effect/packages/effect/src/http/HttpServerRespondable.ts b/packages/effect/src/http/HttpServerRespondable.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServerRespondable.ts rename to packages/effect/src/http/HttpServerRespondable.ts diff --git a/repos/effect/packages/effect/src/http/HttpServerResponse.ts b/packages/effect/src/http/HttpServerResponse.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServerResponse.ts rename to packages/effect/src/http/HttpServerResponse.ts diff --git a/repos/effect/packages/effect/src/http/HttpStaticServer.ts b/packages/effect/src/http/HttpStaticServer.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpStaticServer.ts rename to packages/effect/src/http/HttpStaticServer.ts diff --git a/repos/effect/packages/effect/src/http/HttpStatus.ts b/packages/effect/src/http/HttpStatus.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpStatus.ts rename to packages/effect/src/http/HttpStatus.ts diff --git a/repos/effect/packages/effect/src/http/HttpTraceContext.ts b/packages/effect/src/http/HttpTraceContext.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpTraceContext.ts rename to packages/effect/src/http/HttpTraceContext.ts diff --git a/repos/effect/packages/effect/src/http/Mime.ts b/packages/effect/src/http/Mime.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Mime.ts rename to packages/effect/src/http/Mime.ts diff --git a/repos/effect/packages/effect/src/http/Multipart.ts b/packages/effect/src/http/Multipart.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Multipart.ts rename to packages/effect/src/http/Multipart.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser.ts b/packages/effect/src/http/MultipartParser.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser.ts rename to packages/effect/src/http/MultipartParser.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/HeadersParser.ts b/packages/effect/src/http/MultipartParser/HeadersParser.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/HeadersParser.ts rename to packages/effect/src/http/MultipartParser/HeadersParser.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/Search.ts b/packages/effect/src/http/MultipartParser/Search.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/Search.ts rename to packages/effect/src/http/MultipartParser/Search.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/contentType.ts b/packages/effect/src/http/MultipartParser/internal/contentType.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/contentType.ts rename to packages/effect/src/http/MultipartParser/internal/contentType.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/headers.ts b/packages/effect/src/http/MultipartParser/internal/headers.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/headers.ts rename to packages/effect/src/http/MultipartParser/internal/headers.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/multipart.ts b/packages/effect/src/http/MultipartParser/internal/multipart.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/multipart.ts rename to packages/effect/src/http/MultipartParser/internal/multipart.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/search.ts b/packages/effect/src/http/MultipartParser/internal/search.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/search.ts rename to packages/effect/src/http/MultipartParser/internal/search.ts diff --git a/repos/effect/packages/effect/src/http/Template.ts b/packages/effect/src/http/Template.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Template.ts rename to packages/effect/src/http/Template.ts diff --git a/repos/effect/packages/effect/src/http/Url.ts b/packages/effect/src/http/Url.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Url.ts rename to packages/effect/src/http/Url.ts diff --git a/repos/effect/packages/effect/src/http/UrlParams.ts b/packages/effect/src/http/UrlParams.ts similarity index 100% rename from repos/effect/packages/effect/src/http/UrlParams.ts rename to packages/effect/src/http/UrlParams.ts diff --git a/repos/effect/packages/effect/src/http/index.ts b/packages/effect/src/http/index.ts similarity index 100% rename from repos/effect/packages/effect/src/http/index.ts rename to packages/effect/src/http/index.ts diff --git a/repos/effect/packages/effect/src/http/internal/compression.ts b/packages/effect/src/http/internal/compression.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/compression.ts rename to packages/effect/src/http/internal/compression.ts diff --git a/repos/effect/packages/effect/src/http/internal/headers.ts b/packages/effect/src/http/internal/headers.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/headers.ts rename to packages/effect/src/http/internal/headers.ts diff --git a/repos/effect/packages/effect/src/http/internal/httpBody.ts b/packages/effect/src/http/internal/httpBody.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/httpBody.ts rename to packages/effect/src/http/internal/httpBody.ts diff --git a/repos/effect/packages/effect/src/http/internal/mimeTypes.ts b/packages/effect/src/http/internal/mimeTypes.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/mimeTypes.ts rename to packages/effect/src/http/internal/mimeTypes.ts diff --git a/repos/effect/packages/effect/src/http/internal/preResponseHandler.ts b/packages/effect/src/http/internal/preResponseHandler.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/preResponseHandler.ts rename to packages/effect/src/http/internal/preResponseHandler.ts diff --git a/packages/effect/src/http/internal/tracing.ts b/packages/effect/src/http/internal/tracing.ts new file mode 100644 index 000000000..138794d87 --- /dev/null +++ b/packages/effect/src/http/internal/tracing.ts @@ -0,0 +1,52 @@ +import type * as Tracer from "../../Tracer.ts" +import * as Headers from "../Headers.ts" + +const defaultPorts: Record = { + "http:": 80, + "https:": 443, + "ws:": 80, + "wss:": 443 +} + +/** + * Records the `url.*` and `server.*` attributes for a request URL. + * + * @internal + */ +export const addUrlAttributes = ( + span: Tracer.Span, + url: URL +): void => { + const query = url.search.slice(1) + span.attribute("url.full", url.toString()) + span.attribute("url.path", url.pathname) + if (query !== "") { + span.attribute("url.query", query) + } + span.attribute("url.scheme", url.protocol.slice(0, -1)) + if (url.hostname !== "") { + // URL keeps IPv6 brackets in the hostname, but the semantic conventions do not. + span.attribute("server.address", url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname) + const port = url.port === "" ? defaultPorts[url.protocol] : Number(url.port) + if (port !== undefined) { + span.attribute("server.port", port) + } + } +} + +/** @internal */ +export const addHeaderAttributes = ( + span: Tracer.Span, + phase: "request" | "response", + headers: Headers.Headers, + filter: (headerName: string, phase: "request" | "response") => boolean, + redactedNames: ReadonlyArray +): void => { + for (const name in headers) { + if (!filter(name, phase)) continue + span.attribute( + `http.${phase}.header.${name}`, + Headers.isRedactedName(name, redactedNames) ? "" : headers[name] + ) + } +} diff --git a/repos/effect/packages/effect/src/index.ts b/packages/effect/src/index.ts similarity index 74% rename from repos/effect/packages/effect/src/index.ts rename to packages/effect/src/index.ts index 0ef040bba..73f60d5fb 100644 --- a/repos/effect/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -1,29 +1,42 @@ /** + * @stability stable * @since 2.0.0 */ export { /** + * @stability stable + * @category re-exports * @since 2.0.0 */ absurd, /** + * @stability stable + * @category re-exports * @since 2.0.0 */ cast, /** + * @stability stable + * @category re-exports * @since 2.0.0 */ flow, /** + * @stability stable + * @category re-exports * @since 2.0.0 */ hole, /** + * @stability stable + * @category re-exports * @since 2.0.0 */ identity, /** + * @stability stable + * @category re-exports * @since 2.0.0 */ pipe @@ -38,686 +51,829 @@ export { export * as Arbitrary from "./Arbitrary.ts" /** + * @stability stable * @since 2.0.0 */ export * as Array from "./Array.ts" /** + * @stability stable * @since 2.0.0 */ export * as BigDecimal from "./BigDecimal.ts" /** + * @stability stable * @since 2.0.0 */ export * as BigInt from "./BigInt.ts" /** + * @stability stable * @since 2.0.0 */ export * as Boolean from "./Boolean.ts" /** + * @stability stable * @since 2.0.0 */ export * as Brand from "./Brand.ts" /** + * @stability unstable * @since 4.0.0 */ export * as ByteSize from "./ByteSize.ts" /** + * @stability stable * @since 4.0.0 */ export * as Cache from "./Cache.ts" /** + * @stability stable * @since 2.0.0 */ export * as Cause from "./Cause.ts" /** + * @stability stable * @since 2.0.0 */ export * as Channel from "./Channel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as ChannelSchema from "./ChannelSchema.ts" /** + * @stability stable * @since 2.0.0 */ export * as Chunk from "./Chunk.ts" /** + * @stability stable * @since 2.0.0 */ export * as Clock from "./Clock.ts" /** + * @stability stable * @since 4.0.0 */ export * as Combiner from "./Combiner.ts" /** + * @stability stable * @since 4.0.0 */ export * as Config from "./Config.ts" /** + * @stability stable * @since 4.0.0 */ export * as ConfigProvider from "./ConfigProvider.ts" /** + * @stability stable * @since 2.0.0 */ export * as Console from "./Console.ts" /** + * @stability stable * @since 4.0.0 */ export * as Context from "./Context.ts" /** + * @stability stable * @since 2.0.0 */ export * as Cron from "./Cron.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Crypto from "./Crypto.ts" /** + * @stability stable * @since 2.0.0 */ export * as Data from "./Data.ts" /** + * @stability stable * @since 3.6.0 */ export * as DateTime from "./DateTime.ts" /** + * @stability stable * @since 2.0.0 */ export * as Deferred from "./Deferred.ts" /** + * @stability stable * @since 4.0.0 */ export * as Differ from "./Differ.ts" /** + * @stability stable * @since 2.0.0 */ export * as Duration from "./Duration.ts" /** + * @stability stable * @since 2.0.0 */ export * as Effect from "./Effect.ts" /** + * @stability stable * @since 4.0.0 */ export * as Effectable from "./Effectable.ts" /** + * @stability stable * @since 2.0.0 */ export * as Equal from "./Equal.ts" /** + * @stability stable * @since 2.0.0 */ export * as Equivalence from "./Equivalence.ts" /** + * @stability unstable * @since 4.0.0 */ export * as ErrorReporter from "./ErrorReporter.ts" /** + * @stability unstable * @since 3.16.0 */ export * as ExecutionPlan from "./ExecutionPlan.ts" /** + * @stability stable * @since 2.0.0 */ export * as Exit from "./Exit.ts" /** + * @stability stable * @since 2.0.0 */ export * as Fiber from "./Fiber.ts" /** + * @stability stable * @since 2.0.0 */ export * as FiberHandle from "./FiberHandle.ts" /** + * @stability stable * @since 2.0.0 */ export * as FiberMap from "./FiberMap.ts" /** + * @stability stable * @since 2.0.0 */ export * as FiberSet from "./FiberSet.ts" /** + * @stability unstable * @since 4.0.0 */ export * as FileSystem from "./FileSystem.ts" /** + * @stability stable * @since 4.0.0 */ export * as Filter from "./Filter.ts" /** + * @stability stable * @since 4.0.0 */ export * as Formatter from "./Formatter.ts" /** + * @stability stable * @since 2.0.0 */ export * as Function from "./Function.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Graph from "./Graph.ts" /** + * @stability stable * @since 2.0.0 */ export * as Hash from "./Hash.ts" /** + * @stability stable * @since 2.0.0 */ export * as HashMap from "./HashMap.ts" /** + * @stability unstable * @since 4.0.0 */ export * as HashRing from "./HashRing.ts" /** + * @stability stable * @since 2.0.0 */ export * as HashSet from "./HashSet.ts" /** + * @stability stable * @since 2.0.0 */ export * as HKT from "./HKT.ts" /** + * @stability stable * @since 2.0.0 */ export * as Inspectable from "./Inspectable.ts" /** + * @stability stable * @since 2.0.0 */ export * as Iterable from "./Iterable.ts" /** + * @stability stable * @since 4.0.0 */ export * as JsonPatch from "./JsonPatch.ts" /** + * @stability stable * @since 4.0.0 */ export * as JsonPointer from "./JsonPointer.ts" /** + * @stability stable * @since 4.0.0 */ export * as JsonSchema from "./JsonSchema.ts" /** + * @stability stable * @since 4.0.0 */ export * as Latch from "./Latch.ts" /** + * @stability stable * @since 2.0.0 */ export * as Layer from "./Layer.ts" /** + * @stability unstable * @since 3.14.0 */ export * as LayerMap from "./LayerMap.ts" /** + * @stability unstable * @since 4.0.0 */ export * as LayerRef from "./LayerRef.ts" /** + * @stability stable * @since 2.0.0 */ export * as Logger from "./Logger.ts" /** + * @stability stable * @since 2.0.0 */ export * as LogLevel from "./LogLevel.ts" /** + * @stability stable * @since 2.0.0 */ export * as ManagedRuntime from "./ManagedRuntime.ts" /** + * @stability stable * @since 4.0.0 */ export * as Match from "./Match.ts" /** + * @stability stable * @since 2.0.0 */ export * as Metric from "./Metric.ts" /** + * @stability stable * @since 2.0.0 */ export * as MutableHashMap from "./MutableHashMap.ts" /** + * @stability stable * @since 2.0.0 */ export * as MutableHashSet from "./MutableHashSet.ts" /** + * @stability stable * @since 4.0.0 */ export * as MutableList from "./MutableList.ts" /** + * @stability stable * @since 2.0.0 */ export * as MutableRef from "./MutableRef.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Newtype from "./Newtype.ts" /** + * @stability stable * @since 2.0.0 */ export * as NonEmptyIterable from "./NonEmptyIterable.ts" /** + * @stability stable * @since 2.0.0 */ export * as Number from "./Number.ts" /** + * @stability stable * @since 4.0.0 */ export * as Optic from "./Optic.ts" /** + * @stability stable * @since 2.0.0 */ export * as Option from "./Option.ts" /** + * @stability stable * @since 2.0.0 */ export * as Order from "./Order.ts" /** + * @stability stable * @since 2.0.0 */ export * as Ordering from "./Ordering.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PartitionedSemaphore from "./PartitionedSemaphore.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Path from "./Path.ts" /** + * @stability stable * @since 2.0.0 */ export * as Pipeable from "./Pipeable.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PlatformError from "./PlatformError.ts" /** + * @stability stable * @since 2.0.0 */ export * as Pool from "./Pool.ts" /** + * @stability stable * @since 2.0.0 */ export * as Predicate from "./Predicate.ts" /** + * @stability stable * @since 2.0.0 */ export * as PrimaryKey from "./PrimaryKey.ts" /** + * @stability stable * @since 2.0.0 */ export * as PubSub from "./PubSub.ts" /** + * @stability stable * @since 4.0.0 */ export * as Pull from "./Pull.ts" /** + * @stability stable * @since 3.8.0 */ export * as Queue from "./Queue.ts" /** + * @stability stable * @since 4.0.0 */ export * as Random from "./Random.ts" /** + * @stability stable * @since 3.5.0 */ export * as RcMap from "./RcMap.ts" /** + * @stability stable * @since 3.5.0 */ export * as RcRef from "./RcRef.ts" /** + * @stability stable * @since 2.0.0 */ export * as Record from "./Record.ts" /** + * @stability stable * @since 4.0.0 */ export * as Redactable from "./Redactable.ts" /** + * @stability stable * @since 3.3.0 */ export * as Redacted from "./Redacted.ts" /** + * @stability stable * @since 4.0.0 */ export * as Reducer from "./Reducer.ts" /** + * @stability stable * @since 2.0.0 */ export * as Ref from "./Ref.ts" /** + * @stability stable * @since 4.0.0 */ export * as References from "./References.ts" /** + * @stability stable * @since 2.0.0 */ export * as RegExp from "./RegExp.ts" /** + * @stability stable * @since 2.0.0 */ export * as Request from "./Request.ts" /** + * @stability stable * @since 2.0.0 */ export * as RequestResolver from "./RequestResolver.ts" /** + * @stability stable * @since 2.0.0 */ export * as Resource from "./Resource.ts" /** + * @stability stable * @since 4.0.0 */ export * as Result from "./Result.ts" /** + * @stability stable * @since 4.0.0 */ export * as Runtime from "./Runtime.ts" /** + * @stability stable * @since 2.0.0 */ export * as Schedule from "./Schedule.ts" /** + * @stability stable * @since 2.0.0 */ export * as Scheduler from "./Scheduler.ts" /** + * @stability stable * @since 4.0.0 */ export * as Schema from "./Schema.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaAST from "./SchemaAST.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaGetter from "./SchemaGetter.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaIssue from "./SchemaIssue.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaParser from "./SchemaParser.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaRepresentation from "./SchemaRepresentation.ts" /** + * @stability stable * @since 4.0.0 */ export * as SchemaTransformation from "./SchemaTransformation.ts" /** + * @stability stable * @since 2.0.0 */ export * as Scope from "./Scope.ts" /** + * @stability stable * @since 4.0.0 */ export * as ScopedCache from "./ScopedCache.ts" /** + * @stability stable * @since 2.0.0 */ export * as ScopedRef from "./ScopedRef.ts" /** + * @stability stable * @since 4.0.0 */ export * as Semaphore from "./Semaphore.ts" /** + * @stability stable * @since 2.0.0 */ export * as Sink from "./Sink.ts" /** + * @stability stable * @since 4.0.0 */ export * as StandardSchema from "./StandardSchema.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Stdio from "./Stdio.ts" /** + * @stability stable * @since 2.0.0 */ export * as Stream from "./Stream.ts" /** + * @stability stable * @since 2.0.0 */ export * as String from "./String.ts" /** + * @stability stable * @since 2.0.0 */ export * as Struct from "./Struct.ts" /** + * @stability stable * @since 2.0.0 */ export * as SubscriptionRef from "./SubscriptionRef.ts" /** + * @stability stable * @since 2.0.0 */ export * as Symbol from "./Symbol.ts" /** + * @stability stable * @since 2.0.0 */ export * as SynchronizedRef from "./SynchronizedRef.ts" /** + * @stability stable * @since 2.0.0 */ export * as Take from "./Take.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Terminal from "./Terminal.ts" /** + * @stability stable * @since 2.0.0 */ export * as Tracer from "./Tracer.ts" /** + * @stability stable * @since 2.0.0 */ export * as Trie from "./Trie.ts" /** + * @stability stable * @since 2.0.0 */ export * as Tuple from "./Tuple.ts" /** + * @stability unstable * @since 4.0.0 */ export * as TxChunk from "./TxChunk.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxDeferred from "./TxDeferred.ts" /** + * @stability stable * @since 2.0.0 */ export * as TxHashMap from "./TxHashMap.ts" /** + * @stability stable * @since 2.0.0 */ export * as TxHashSet from "./TxHashSet.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxPriorityQueue from "./TxPriorityQueue.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxPubSub from "./TxPubSub.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxQueue from "./TxQueue.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxReentrantLock from "./TxReentrantLock.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxRef from "./TxRef.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxSemaphore from "./TxSemaphore.ts" /** + * @stability stable * @since 4.0.0 */ export * as TxSubscriptionRef from "./TxSubscriptionRef.ts" /** + * @stability stable * @since 4.0.0 */ export * as Types from "./Types.ts" /** + * @stability stable * @since 4.0.0 */ export * as UndefinedOr from "./UndefinedOr.ts" /** + * @stability stable * @since 2.0.0 */ export * as Unify from "./Unify.ts" /** + * @stability stable * @since 2.0.0 */ export * as Utils from "./Utils.ts" + +/** + * @stability unstable + * @since 4.0.2 + */ +export * as Version from "./Version.ts" diff --git a/repos/effect/packages/effect/src/internal/arbitrary/array.ts b/packages/effect/src/internal/arbitrary/array.ts similarity index 96% rename from repos/effect/packages/effect/src/internal/arbitrary/array.ts rename to packages/effect/src/internal/arbitrary/array.ts index 85d3c1d08..6e5547b69 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/array.ts +++ b/packages/effect/src/internal/arbitrary/array.ts @@ -54,13 +54,13 @@ function rebuild( } if (index >= elementStart) itemShrinks ??= children[index].shrinks?.() if (itemShrinks !== undefined) { - return Effect.matchEffect(itemShrinks, { - onFailure: () => { + return Effect.matchCauseEffect(itemShrinks, { + onFailure: Model.onTypedFailure(() => { itemShrinks = undefined removable = undefined index++ return loop() - }, + }), onSuccess: (item): Effect.Effect>> => { if (item._tag === "Discarded") return Effect.succeed(item) return Effect.succeed(rebuild(InternalArray.replaceAt(children, index, item), shape, index)) diff --git a/repos/effect/packages/effect/src/internal/arbitrary/bigDecimal.ts b/packages/effect/src/internal/arbitrary/bigDecimal.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/bigDecimal.ts rename to packages/effect/src/internal/arbitrary/bigDecimal.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/bigInt.ts b/packages/effect/src/internal/arbitrary/bigInt.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/bigInt.ts rename to packages/effect/src/internal/arbitrary/bigInt.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/model.ts b/packages/effect/src/internal/arbitrary/model.ts similarity index 95% rename from repos/effect/packages/effect/src/internal/arbitrary/model.ts rename to packages/effect/src/internal/arbitrary/model.ts index f6d017433..b095cd028 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/model.ts +++ b/packages/effect/src/internal/arbitrary/model.ts @@ -1,12 +1,32 @@ -import type * as Cause from "../../Cause.ts" +import * as Cause from "../../Cause.ts" import * as Effect from "../../Effect.ts" import * as Option from "../../Option.ts" +import * as Result from "../../Result.ts" import * as InternalArray from "../array.ts" import { done } from "../core.ts" /** @internal */ export type ShrinkPull = Effect.Effect +/** + * Builds a cause handler that recovers typed failures, or propagates only + * defects and interruptions, preserving their original annotations. + * + * @internal + */ +export const onTypedFailure = ( + f: (error: E) => Effect.Effect +) => +(cause: Cause.Cause): Effect.Effect => { + if (!cause.reasons.every(Cause.isFailReason)) { + return Effect.failCause(Cause.fromReasons(cause.reasons.filter( + (reason): reason is Cause.Die | Cause.Interrupt => reason._tag !== "Fail" + ))) + } + const error = Cause.findError(cause) + return Result.isFailure(error) ? Effect.failCause(error.failure) : f(error.success) +} + /** @internal */ export interface Sample { readonly _tag: "Generated" @@ -129,10 +149,13 @@ export function concatPulls(pulls: ReadonlyArray>): ShrinkPull< Effect.suspend(() => index >= pulls.length ? done() - : Effect.catch(pulls[index], () => { - index++ - return loop() - }) + : Effect.catchCause( + pulls[index], + onTypedFailure(() => { + index++ + return loop() + }) + ) ) return loop() } @@ -182,11 +205,11 @@ export function retain(sample: Sample): Retained { return Effect.suspend(() => { if (history !== undefined && index < history.length) return Effect.succeed(history[index++]) if (ended) return done() - return Effect.matchEffect(source, { - onFailure: () => { + return Effect.matchCauseEffect(source, { + onFailure: onTypedFailure(() => { ended = true return done() - }, + }), onSuccess: (attempt) => { const item = attempt._tag === "Discarded" ? attempt : retain(attempt) history ??= [] @@ -288,11 +311,11 @@ function filterMapPull( Effect.suspend(() => { const current = stack[stack.length - 1] if (current === undefined) return done() - return Effect.matchEffect(current, { - onFailure: () => { + return Effect.matchCauseEffect(current, { + onFailure: onTypedFailure(() => { stack.pop() return loop() - }, + }), onSuccess: (attempt) => { if (attempt._tag === "Discarded") return Effect.succeed>(attempt) const sample = attempt @@ -318,11 +341,11 @@ function filterPull(source: ShrinkPull>, predicate: (value: A) => Effect.suspend(() => { const current = stack[stack.length - 1] if (current === undefined) return done() - return Effect.matchEffect(current, { - onFailure: () => { + return Effect.matchCauseEffect(current, { + onFailure: onTypedFailure(() => { stack.pop() return loop() - }, + }), onSuccess: (attempt) => { if (attempt._tag === "Discarded") return Effect.succeed>(attempt) const sample = attempt diff --git a/repos/effect/packages/effect/src/internal/arbitrary/regexp.ts b/packages/effect/src/internal/arbitrary/regexp.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/regexp.ts rename to packages/effect/src/internal/arbitrary/regexp.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/runner.ts b/packages/effect/src/internal/arbitrary/runner.ts similarity index 98% rename from repos/effect/packages/effect/src/internal/arbitrary/runner.ts rename to packages/effect/src/internal/arbitrary/runner.ts index f9c68d284..05d3426ee 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/runner.ts +++ b/packages/effect/src/internal/arbitrary/runner.ts @@ -383,11 +383,11 @@ function flatMapSourcePull( Effect.suspend(() => { const current = stack[stack.length - 1] if (current === undefined) return done() - return Effect.matchEffect(current, { - onFailure: () => { + return Effect.matchCauseEffect(current, { + onFailure: Model.onTypedFailure(() => { stack.pop() return loop() - }, + }), onSuccess: (sourceAttempt) => { if (sourceAttempt._tag === "Discarded") return Effect.succeed>(sourceAttempt) const sourceSample = sourceAttempt @@ -505,14 +505,14 @@ const evaluateProperty = ( ): Effect.Effect, never, R> => { const output = property(value) if (!Effect.isEffect(output)) return Effect.succeed(output === true ? passedProperty : returnedFalse) - return Effect.matchEager(output, { - onFailure: (error): PropertyError => ({ _tag: "PropertyError", error }), - onSuccess: (success) => success === true ? passedProperty : returnedFalse + return Effect.matchCauseEffectEager(output, { + onFailure: Model.onTypedFailure((error: E) => Effect.succeed>({ _tag: "PropertyError", error })), + onSuccess: (success) => Effect.succeed(success === true ? passedProperty : returnedFalse) }) } const pullNext = (pull: Model.ShrinkPull>): Effect.Effect | undefined> => - Effect.catch(pull, () => Effect.succeed(undefined)) + Effect.catchCause(pull, Model.onTypedFailure(() => Effect.succeed(undefined))) const shrink = Effect.fnUntraced(function*( initial: Model.Sample, diff --git a/repos/effect/packages/effect/src/internal/arbitrary/schema.ts b/packages/effect/src/internal/arbitrary/schema.ts similarity index 99% rename from repos/effect/packages/effect/src/internal/arbitrary/schema.ts rename to packages/effect/src/internal/arbitrary/schema.ts index f739349cb..9183848c3 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/schema.ts +++ b/packages/effect/src/internal/arbitrary/schema.ts @@ -30,10 +30,11 @@ interface Checks { const infinity = Number.POSITIVE_INFINITY const finiteNumberConstraint: FilterConstraint = { number: "finite" } -const optionMatch = { onFailure: Option.none, onSuccess: Option.some } - const optionComputation = (self: Effect.Effect): Model.Computation> => { - const result = Effect.matchEager(self, optionMatch) as Effect.Effect> + const result = Effect.matchCauseEffectEager(self, { + onFailure: Model.onTypedFailure(() => Effect.succeedNone), + onSuccess: Effect.succeedSome + }) as Effect.Effect> return effectIsExit(result) && result._tag === "Success" ? result.value : result } diff --git a/repos/effect/packages/effect/src/internal/array.ts b/packages/effect/src/internal/array.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/array.ts rename to packages/effect/src/internal/array.ts diff --git a/repos/effect/packages/effect/src/internal/core.ts b/packages/effect/src/internal/core.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/core.ts rename to packages/effect/src/internal/core.ts diff --git a/repos/effect/packages/effect/src/internal/count.ts b/packages/effect/src/internal/count.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/count.ts rename to packages/effect/src/internal/count.ts diff --git a/repos/effect/packages/effect/src/internal/dateTime.ts b/packages/effect/src/internal/dateTime.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/dateTime.ts rename to packages/effect/src/internal/dateTime.ts diff --git a/repos/effect/packages/effect/src/internal/doNotation.ts b/packages/effect/src/internal/doNotation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/doNotation.ts rename to packages/effect/src/internal/doNotation.ts diff --git a/repos/effect/packages/effect/src/internal/effect.ts b/packages/effect/src/internal/effect.ts similarity index 99% rename from repos/effect/packages/effect/src/internal/effect.ts rename to packages/effect/src/internal/effect.ts index 874e545e1..f6343b78b 100644 --- a/repos/effect/packages/effect/src/internal/effect.ts +++ b/packages/effect/src/internal/effect.ts @@ -345,6 +345,8 @@ export const causePrettyError = ( error = new globalThis.Error( !original ? `Unknown error: ${original}` : kind === "string" ? original as any : formatJson(original) ) + const stack = `${error.name}: ${error.message}` + error.stack = annotations ? addStackAnnotations(stack, annotations) : stack } return error } @@ -629,44 +631,46 @@ export class FiberImpl implements Fiber.Fiber { this.currentOpCount = 0 try { while (true) { - if (this._deferredInterrupt) { - this._deferredInterrupt = false - current = failCause(this._interruptedCause!) as any - } - this.currentOpCount++ - // Refresh the cache because a primitive can replace the fiber context. - const cache = this.cache - if ( - !yielding && - !cache.preventYield && - cache.scheduler.shouldYield(this as any) - ) { - yielding = true - const prev = current - current = flatMap(yieldNow, () => prev as any) as any - } - current = cache.tracerContext - ? cache.tracerContext(current as any, this) - : (current as any)[evaluate](this) - if (current === Yield) { - const yielded = this._yielded! - if (ExitTypeId in yielded) { + try { + if (this._deferredInterrupt) { this._deferredInterrupt = false - this._yielded = undefined - return yielded - } else if (this._deferredInterrupt) { - this._yielded = undefined - yielded() - continue + current = failCause(this._interruptedCause!) as any + } + this.currentOpCount++ + // Refresh the cache because a primitive can replace the fiber context. + const cache = this.cache + if ( + !yielding && + !cache.preventYield && + cache.scheduler.shouldYield(this as any) + ) { + yielding = true + const prev = current + current = flatMap(yieldNow, () => prev as any) as any + } + current = cache.tracerContext + ? cache.tracerContext(current as any, this) + : (current as any)[evaluate](this) + if (current === Yield) { + const yielded = this._yielded! + if (ExitTypeId in yielded) { + this._deferredInterrupt = false + this._yielded = undefined + return yielded + } else if (this._deferredInterrupt) { + this._yielded = undefined + yielded() + continue + } + return Yield + } + } catch (error) { + if (!hasProperty(current, evaluate)) { + return exitDie(`Fiber.runLoop: Not a valid effect: ${String(current)}`) } - return Yield + current = exitDie(error) as any } } - } catch (error) { - if (!hasProperty(current, evaluate)) { - return exitDie(`Fiber.runLoop: Not a valid effect: ${String(current)}`) - } - return this.runLoop(exitDie(error) as any) } finally { this._running = prevRunning ;(globalThis as any)[currentFiberTypeId] = prevFiber @@ -3928,10 +3932,8 @@ export const scopeTag: Context.Service = Context.Servi /** @internal */ export const scopeClose = (self: Scope.Scope, exit_: Exit.Exit) => withFiber((fiber) => { - const close = scopeCloseUnsafe(self, exit_) - if (close === undefined) return void_ fiberEnterUninterruptibleUnsafe(fiber) - return close + return scopeCloseUnsafe(self, exit_) ?? void_ }) /** @internal */ @@ -5524,6 +5526,10 @@ export const forkUnsafe = ( const parentRuntime = parent as FiberImpl const interruptible = uninterruptible === "inherit" ? parentRuntime.interruptible : !uninterruptible const child = new FiberImpl(parentRuntime.context, interruptible) + if (!daemon) { + parentRuntime.children().add(child) + child._parent = parentRuntime + } if (immediate) { child.evaluate(effect as any) } else { @@ -5531,10 +5537,6 @@ export const forkUnsafe = ( child._asyncContext = captureAsyncContext() parentRuntime.currentDispatcher.scheduleTask(() => child.evaluate(effect as any), 0) } - if (!daemon && !child._exit) { - parentRuntime.children().add(child) - child._parent = parentRuntime - } return child } @@ -5711,9 +5713,9 @@ export const fiberRunIn: { self.interruptUnsafe(self.id) return self } - const key = {} - scopeAddFinalizerUnsafe(scope, key, () => fiberInterrupt(self)) - self.addObserver(() => scopeRemoveFinalizerUnsafe(scope, key)) + scopeRemoveFinalizerUnsafe(scope, self) + scopeAddFinalizerUnsafe(scope, self, () => fiberInterrupt(self)) + self.addObserver(() => scopeRemoveFinalizerUnsafe(scope, self)) return self }) @@ -6026,7 +6028,7 @@ export const makeSpanUnsafe = ( links, startTime: timingEnabled ? clock.currentTimeNanosUnsafe() : bigint0, kind: options?.kind ?? "internal", - root: options?.root ?? Option.isNone(parent), + root: options?.root ?? false, sampled: options?.sampled ?? (Option.isSome(parent) && parent.value.sampled === false ? false diff --git a/repos/effect/packages/effect/src/internal/equal.ts b/packages/effect/src/internal/equal.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/equal.ts rename to packages/effect/src/internal/equal.ts diff --git a/repos/effect/packages/effect/src/internal/errors.ts b/packages/effect/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/errors.ts rename to packages/effect/src/internal/errors.ts diff --git a/repos/effect/packages/effect/src/internal/executionPlan.ts b/packages/effect/src/internal/executionPlan.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/executionPlan.ts rename to packages/effect/src/internal/executionPlan.ts diff --git a/repos/effect/packages/effect/src/internal/graph.ts b/packages/effect/src/internal/graph.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/graph.ts rename to packages/effect/src/internal/graph.ts diff --git a/repos/effect/packages/effect/src/internal/graphCsr.ts b/packages/effect/src/internal/graphCsr.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/graphCsr.ts rename to packages/effect/src/internal/graphCsr.ts diff --git a/repos/effect/packages/effect/src/internal/hash.ts b/packages/effect/src/internal/hash.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hash.ts rename to packages/effect/src/internal/hash.ts diff --git a/repos/effect/packages/effect/src/internal/hashMap.ts b/packages/effect/src/internal/hashMap.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hashMap.ts rename to packages/effect/src/internal/hashMap.ts diff --git a/repos/effect/packages/effect/src/internal/hashSet.ts b/packages/effect/src/internal/hashSet.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hashSet.ts rename to packages/effect/src/internal/hashSet.ts diff --git a/repos/effect/packages/effect/src/internal/layer.ts b/packages/effect/src/internal/layer.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/layer.ts rename to packages/effect/src/internal/layer.ts diff --git a/repos/effect/packages/effect/src/internal/matcher.ts b/packages/effect/src/internal/matcher.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/matcher.ts rename to packages/effect/src/internal/matcher.ts diff --git a/repos/effect/packages/effect/src/internal/metric.ts b/packages/effect/src/internal/metric.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/metric.ts rename to packages/effect/src/internal/metric.ts diff --git a/repos/effect/packages/effect/src/internal/option.ts b/packages/effect/src/internal/option.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/option.ts rename to packages/effect/src/internal/option.ts diff --git a/repos/effect/packages/effect/src/internal/persistence.ts b/packages/effect/src/internal/persistence.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/persistence.ts rename to packages/effect/src/internal/persistence.ts diff --git a/repos/effect/packages/effect/src/internal/random.ts b/packages/effect/src/internal/random.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/random.ts rename to packages/effect/src/internal/random.ts diff --git a/repos/effect/packages/effect/src/internal/rcRef.ts b/packages/effect/src/internal/rcRef.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/rcRef.ts rename to packages/effect/src/internal/rcRef.ts diff --git a/repos/effect/packages/effect/src/internal/record.ts b/packages/effect/src/internal/record.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/record.ts rename to packages/effect/src/internal/record.ts diff --git a/repos/effect/packages/effect/src/internal/redacted.ts b/packages/effect/src/internal/redacted.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/redacted.ts rename to packages/effect/src/internal/redacted.ts diff --git a/repos/effect/packages/effect/src/internal/references.ts b/packages/effect/src/internal/references.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/references.ts rename to packages/effect/src/internal/references.ts diff --git a/repos/effect/packages/effect/src/internal/request.ts b/packages/effect/src/internal/request.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/request.ts rename to packages/effect/src/internal/request.ts diff --git a/repos/effect/packages/effect/src/internal/result.ts b/packages/effect/src/internal/result.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/result.ts rename to packages/effect/src/internal/result.ts diff --git a/repos/effect/packages/effect/src/internal/schedule.ts b/packages/effect/src/internal/schedule.ts similarity index 89% rename from repos/effect/packages/effect/src/internal/schedule.ts rename to packages/effect/src/internal/schedule.ts index ecb1c3743..16e7dd591 100644 --- a/repos/effect/packages/effect/src/internal/schedule.ts +++ b/packages/effect/src/internal/schedule.ts @@ -3,12 +3,16 @@ import type { Effect, Repeat, Retry } from "../Effect.ts" import { constant, constTrue, dual, identity } from "../Function.ts" import * as Option from "../Option.ts" import * as Pull from "../Pull.ts" +import * as Result from "../Result.ts" import * as Schedule from "../Schedule.ts" import type { NoInfer } from "../Types.ts" import { internalCall } from "../Utils.ts" import * as core from "./core.ts" import * as effect from "./effect.ts" +const findErrorOnly = (cause: Cause.Cause): Result.Result> => + cause.reasons.every(core.isFailReason) ? effect.findError(cause) : Result.fail(cause as Cause.Cause) + /** @internal */ export const repeatOrElse: { ( @@ -29,21 +33,22 @@ export const repeatOrElse: { ): Effect => effect.flatMap(Schedule.toStepWithMetadata(schedule), (step) => { let meta = Schedule.CurrentMetadata.defaultValue() - return effect.catch_( - effect.forever( - effect.tap( - effect.flatMap(effect.suspend(() => effect.provideService(self, Schedule.CurrentMetadata, meta)), step), - (meta_) => - effect.sync(() => { - meta = meta_ - }) + return effect.catchCauseFilter( + Pull.catchDone( + effect.forever( + effect.tap( + effect.flatMap(effect.suspend(() => effect.provideService(self, Schedule.CurrentMetadata, meta)), step), + (meta_) => + effect.sync(() => { + meta = meta_ + }) + ), + { disableYield: true } ), - { disableYield: true } + (out) => effect.succeed(out as B) ), - (error) => - core.isDone(error) - ? effect.succeed(error.value as B) - : orElse(error as E | E2, meta.attempt === 0 ? Option.none() : Option.some(meta as any)) + findErrorOnly, + (error) => orElse(error, meta.attempt === 0 ? Option.none() : Option.some(meta as any)) ) })) @@ -66,8 +71,9 @@ export const retryOrElse: { effect.flatMap(Schedule.toStepWithMetadata(policy), (step) => { let meta = Schedule.CurrentMetadata.defaultValue() let lastError!: E - const loop: Effect, R | R1> = effect.catch_( + const loop: Effect, R | R1> = effect.catchCauseFilter( effect.suspend(() => effect.provideService(self, Schedule.CurrentMetadata, meta)), + findErrorOnly, (error) => { lastError = error return effect.flatMap(step(error), (meta_) => { @@ -198,7 +204,7 @@ export const scheduleFrom = dual< effect.flatMap(Schedule.toStepWithMetadata(schedule), (step) => { let meta = Schedule.CurrentMetadata.defaultValue() const selfWithMeta = effect.suspend(() => effect.provideService(self, Schedule.CurrentMetadata, meta)) - return effect.catch_( + return Pull.catchDone( effect.flatMap( step(initial), (meta_) => { @@ -210,10 +216,10 @@ export const scheduleFrom = dual< step(meta_) { meta = meta_ } - }) as Effect + }) as Effect, R | Env> } ), - (error) => core.isDone(error) ? effect.succeed(error.value as Output) : effect.fail(error as E | Error) + (out) => effect.succeed(out as Output) ) })) diff --git a/repos/effect/packages/effect/src/internal/schema/annotations.ts b/packages/effect/src/internal/schema/annotations.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/annotations.ts rename to packages/effect/src/internal/schema/annotations.ts diff --git a/repos/effect/packages/effect/src/internal/schema/cause.ts b/packages/effect/src/internal/schema/cause.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/cause.ts rename to packages/effect/src/internal/schema/cause.ts diff --git a/repos/effect/packages/effect/src/internal/schema/codegen.ts b/packages/effect/src/internal/schema/codegen.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/codegen.ts rename to packages/effect/src/internal/schema/codegen.ts diff --git a/repos/effect/packages/effect/src/internal/schema/compilerRegistry.ts b/packages/effect/src/internal/schema/compilerRegistry.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/compilerRegistry.ts rename to packages/effect/src/internal/schema/compilerRegistry.ts diff --git a/repos/effect/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts b/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts rename to packages/effect/src/internal/schema/fromJsonSchemaDocument.ts diff --git a/repos/effect/packages/effect/src/internal/schema/fromRepresentation.ts b/packages/effect/src/internal/schema/fromRepresentation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/fromRepresentation.ts rename to packages/effect/src/internal/schema/fromRepresentation.ts diff --git a/repos/effect/packages/effect/src/internal/schema/interpreter.ts b/packages/effect/src/internal/schema/interpreter.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/interpreter.ts rename to packages/effect/src/internal/schema/interpreter.ts diff --git a/repos/effect/packages/effect/src/internal/schema/make.ts b/packages/effect/src/internal/schema/make.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/make.ts rename to packages/effect/src/internal/schema/make.ts diff --git a/repos/effect/packages/effect/src/internal/schema/parser.ts b/packages/effect/src/internal/schema/parser.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/parser.ts rename to packages/effect/src/internal/schema/parser.ts diff --git a/repos/effect/packages/effect/src/internal/schema/standardSchema.ts b/packages/effect/src/internal/schema/standardSchema.ts similarity index 98% rename from repos/effect/packages/effect/src/internal/schema/standardSchema.ts rename to packages/effect/src/internal/schema/standardSchema.ts index 66147c958..d575bfe03 100644 --- a/repos/effect/packages/effect/src/internal/schema/standardSchema.ts +++ b/packages/effect/src/internal/schema/standardSchema.ts @@ -53,7 +53,7 @@ export function toStandardSchemaV1>( }) }) } - if ("~standard" in self) { + if (Object.hasOwn(self, "~standard")) { const out = self as any if ("validate" in out["~standard"]) return out Object.assign(out["~standard"], { validate }) @@ -109,7 +109,7 @@ export function toStandardJSONSchemaV1( return toBaseStandardJSONSchemaV1(SchemaAST.toType(self.ast), options.target) } } - if ("~standard" in self) { + if (Object.hasOwn(self, "~standard")) { const out = self as any if ("jsonSchema" in out["~standard"]) return out Object.assign(out["~standard"], { jsonSchema }) diff --git a/repos/effect/packages/effect/src/internal/schema/toCodeDocument.ts b/packages/effect/src/internal/schema/toCodeDocument.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toCodeDocument.ts rename to packages/effect/src/internal/schema/toCodeDocument.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toCodec.ts b/packages/effect/src/internal/schema/toCodec.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toCodec.ts rename to packages/effect/src/internal/schema/toCodec.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toDifferJsonPatch.ts b/packages/effect/src/internal/schema/toDifferJsonPatch.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toDifferJsonPatch.ts rename to packages/effect/src/internal/schema/toDifferJsonPatch.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toEncoderXml.ts b/packages/effect/src/internal/schema/toEncoderXml.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toEncoderXml.ts rename to packages/effect/src/internal/schema/toEncoderXml.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toEquivalence.ts b/packages/effect/src/internal/schema/toEquivalence.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toEquivalence.ts rename to packages/effect/src/internal/schema/toEquivalence.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toFormatter.ts b/packages/effect/src/internal/schema/toFormatter.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toFormatter.ts rename to packages/effect/src/internal/schema/toFormatter.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toIso.ts b/packages/effect/src/internal/schema/toIso.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toIso.ts rename to packages/effect/src/internal/schema/toIso.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toJsonSchemaDocument.ts b/packages/effect/src/internal/schema/toJsonSchemaDocument.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toJsonSchemaDocument.ts rename to packages/effect/src/internal/schema/toJsonSchemaDocument.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toRepresentation.ts b/packages/effect/src/internal/schema/toRepresentation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toRepresentation.ts rename to packages/effect/src/internal/schema/toRepresentation.ts diff --git a/repos/effect/packages/effect/src/internal/schemaError.ts b/packages/effect/src/internal/schemaError.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schemaError.ts rename to packages/effect/src/internal/schemaError.ts diff --git a/repos/effect/packages/effect/src/internal/stackTraceLimit.ts b/packages/effect/src/internal/stackTraceLimit.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/stackTraceLimit.ts rename to packages/effect/src/internal/stackTraceLimit.ts diff --git a/repos/effect/packages/effect/src/internal/stream.ts b/packages/effect/src/internal/stream.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/stream.ts rename to packages/effect/src/internal/stream.ts diff --git a/repos/effect/packages/effect/src/internal/tracer.ts b/packages/effect/src/internal/tracer.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/tracer.ts rename to packages/effect/src/internal/tracer.ts diff --git a/repos/effect/packages/effect/src/internal/trie.ts b/packages/effect/src/internal/trie.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/trie.ts rename to packages/effect/src/internal/trie.ts diff --git a/repos/effect/packages/effect/src/internal/ulid.ts b/packages/effect/src/internal/ulid.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/ulid.ts rename to packages/effect/src/internal/ulid.ts diff --git a/repos/effect/packages/effect/src/internal/uuid.ts b/packages/effect/src/internal/uuid.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/uuid.ts rename to packages/effect/src/internal/uuid.ts diff --git a/packages/effect/src/internal/version.ts b/packages/effect/src/internal/version.ts new file mode 100644 index 000000000..dae214bd4 --- /dev/null +++ b/packages/effect/src/internal/version.ts @@ -0,0 +1,2 @@ +/** @internal */ +export const version = "4.0.2" diff --git a/repos/effect/packages/effect/src/net/IpInterface.ts b/packages/effect/src/net/IpInterface.ts similarity index 100% rename from repos/effect/packages/effect/src/net/IpInterface.ts rename to packages/effect/src/net/IpInterface.ts diff --git a/repos/effect/packages/effect/src/net/IpNetwork.ts b/packages/effect/src/net/IpNetwork.ts similarity index 100% rename from repos/effect/packages/effect/src/net/IpNetwork.ts rename to packages/effect/src/net/IpNetwork.ts diff --git a/repos/effect/packages/effect/src/net/NetAddress.ts b/packages/effect/src/net/NetAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/net/NetAddress.ts rename to packages/effect/src/net/NetAddress.ts diff --git a/repos/effect/packages/effect/src/net/index.ts b/packages/effect/src/net/index.ts similarity index 100% rename from repos/effect/packages/effect/src/net/index.ts rename to packages/effect/src/net/index.ts diff --git a/repos/effect/packages/effect/src/observability/Otlp.ts b/packages/effect/src/observability/Otlp.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/Otlp.ts rename to packages/effect/src/observability/Otlp.ts diff --git a/repos/effect/packages/effect/src/observability/OtlpExporter.ts b/packages/effect/src/observability/OtlpExporter.ts similarity index 95% rename from repos/effect/packages/effect/src/observability/OtlpExporter.ts rename to packages/effect/src/observability/OtlpExporter.ts index 385d9555f..5b749b870 100644 --- a/repos/effect/packages/effect/src/observability/OtlpExporter.ts +++ b/packages/effect/src/observability/OtlpExporter.ts @@ -26,6 +26,7 @@ import * as Num from "../Number.ts" import * as Option from "../Option.ts" import * as Schedule from "../Schedule.ts" import * as Scope from "../Scope.ts" +import * as Version from "../Version.ts" const retryAfterDelay = (value: string | undefined): Effect.Effect => { const seconds = Option.fromUndefinedOr(value).pipe(Option.flatMap(Num.parse)) @@ -194,12 +195,14 @@ export const make: ( HttpClient.retryTransient({ schedule: policy, times: 3 }) ) - let headers = Headers.fromRecordUnsafe({ - "user-agent": `effect-opentelemetry-${options.label}/0.0.0` - }) - if (options.headers) { - headers = Headers.merge(Headers.fromInput(options.headers), headers) - } + const exporterUserAgent = `OTel-OTLP-Exporter-JavaScript-Effect-${options.label}/${Version.getCurrentVersion()}` + const userHeaders = Headers.fromInput(options.headers) + const userAgent = userHeaders["user-agent"] + const headers = Headers.set( + userHeaders, + "user-agent", + userAgent ? `${userAgent} ${exporterUserAgent}` : exporterUserAgent + ) const request = HttpClientRequest.post(options.url, { headers }) let buffer: Array = [] diff --git a/repos/effect/packages/effect/src/observability/OtlpLogger.ts b/packages/effect/src/observability/OtlpLogger.ts similarity index 90% rename from repos/effect/packages/effect/src/observability/OtlpLogger.ts rename to packages/effect/src/observability/OtlpLogger.ts index cb63bd3f0..fa5e97ce5 100644 --- a/repos/effect/packages/effect/src/observability/OtlpLogger.ts +++ b/packages/effect/src/observability/OtlpLogger.ts @@ -23,6 +23,7 @@ import type * as LogLevel from "../LogLevel.ts" import * as Option from "../Option.ts" import { CurrentLogAnnotations, CurrentLogSpans } from "../References.ts" import type * as Scope from "../Scope.ts" +import * as Version from "../Version.ts" import * as OtlpEnv from "./internal/otlpEnv.ts" import * as Exporter from "./OtlpExporter.ts" import type { AnyValue, Fixed64, KeyValue, Resource } from "./OtlpResource.ts" @@ -64,7 +65,8 @@ export const make: ( const serialization = yield* OtlpSerialization const otelResource = yield* OtlpResource.fromConfig(options.resource) const scope: IInstrumentationScope = { - name: OtlpResource.serviceNameUnsafe(otelResource) + name: "effect", + version: Version.getCurrentVersion() } const exporter = yield* Exporter.make({ @@ -192,32 +194,29 @@ export interface LogsData { // internal +const nanosPerMilli = BigInt(1_000_000) + const makeLogRecord = (options: Logger.Options, opts: { readonly excludeLogSpans: boolean readonly clock: Clock }): ILogRecord => { - const now = opts.clock.currentTimeNanosUnsafe() - const nanosString = now.toString() - const nowMillis = options.date.getTime() + const observedTime = opts.clock.currentTimeNanosUnsafe().toString() + const eventMillis = options.date.getTime() + const eventTime = (BigInt(eventMillis) * nanosPerMilli).toString() - const attributes = OtlpResource.entriesToAttributes(Object.entries(options.fiber.getRef(CurrentLogAnnotations))) - attributes.push({ - key: "fiberId", - value: { intValue: options.fiber.id } - }) + const attributes: Record = { ...options.fiber.getRef(CurrentLogAnnotations) } + attributes["effect.fiberId"] = options.fiber.id if (!opts.excludeLogSpans) { + // Outermost spans win duplicate labels. for (const [label, startTime] of options.fiber.getRef(CurrentLogSpans)) { - attributes.push({ - key: `logSpan.${label}`, - value: { stringValue: `${nowMillis - startTime}ms` } - }) + attributes[`effect.log_span.${label}`] = eventMillis - startTime } } - if (options.cause.reasons.length > 0) { - attributes.push({ - key: "log.error", - value: { stringValue: Cause.pretty(options.cause) } - }) + const errors = Cause.prettyErrors(options.cause, { includeCauseInStack: true }) + if (errors.length > 0) { + attributes["exception.type"] = errors[0].name + attributes["exception.message"] = errors[0].message + attributes["exception.stacktrace"] = errors.map((error) => error.stack).join("\n") } const message = Arr.ensure(options.message) @@ -225,9 +224,9 @@ const makeLogRecord = (options: Logger.Options, opts: { const logRecord: ILogRecord = { severityNumber: logLevelToSeverityNumber(options.logLevel), severityText: options.logLevel, - timeUnixNano: nanosString, - observedTimeUnixNano: nanosString, - attributes, + timeUnixNano: eventTime, + observedTimeUnixNano: observedTime, + attributes: OtlpResource.entriesToAttributes(Object.entries(attributes)), body: OtlpResource.unknownToAttributeValue(message.length === 1 ? message[0] : message), droppedAttributesCount: 0 } diff --git a/repos/effect/packages/effect/src/observability/OtlpMetrics.ts b/packages/effect/src/observability/OtlpMetrics.ts similarity index 88% rename from repos/effect/packages/effect/src/observability/OtlpMetrics.ts rename to packages/effect/src/observability/OtlpMetrics.ts index f3cb68a4e..a9a8296ea 100644 --- a/repos/effect/packages/effect/src/observability/OtlpMetrics.ts +++ b/packages/effect/src/observability/OtlpMetrics.ts @@ -39,6 +39,8 @@ import { OtlpSerialization } from "./OtlpSerialization.ts" * * `"delta"` reports changes since the last export. Each interval is independent with no dependency on previous measurements. * + * Summaries are always cumulative, regardless of this setting. + * * **Example** (Configuring aggregation temporality) * * ```ts import.meta.vitest @@ -114,7 +116,6 @@ export const make: (options: { let previousCounterState = new Map() let previousHistogramState = new Map() let previousFrequencyState = new Map>() - let previousSummaryState = new Map() let snapshotSequence = 0 let committedSnapshotSequence = -1 @@ -126,7 +127,6 @@ export const make: (options: { const nextCounterState = new Map(previousCounterState) const nextHistogramState = new Map(previousHistogramState) const nextFrequencyState = new Map(previousFrequencyState) - const nextSummaryState = new Map(previousSummaryState) const metricData: Array = [] const metricDataByName = new Map() const addMetricData = (data: IMetric) => { @@ -144,7 +144,7 @@ export const make: (options: { for (let i = 0, len = snapshot.length; i < len; i++) { const state = snapshot[i] - const unit = state.attributes?.unit ?? state.attributes?.time_unit ?? "1" + const unit = toUcumUnit(state.attributes?.unit ?? state.attributes?.time_unit) const attributes = state.attributes ? OtlpResource.entriesToAttributes(Object.entries(state.attributes)) : [] const metricKey = makeMetricKey(state.id, state.attributes) @@ -181,7 +181,7 @@ export const make: (options: { timeUnixNano: nowTime } if (typeof reportValue === "bigint") { - dataPoint.asInt = Number(reportValue) + dataPoint.asInt = bigintToInt64(reportValue) } else { dataPoint.asDouble = reportValue } @@ -209,7 +209,7 @@ export const make: (options: { timeUnixNano: nowTime } if (typeof state.state.value === "bigint") { - dataPoint.asInt = Number(state.state.value) + dataPoint.asInt = bigintToInt64(state.state.value) } else { dataPoint.asDouble = state.state.value } @@ -344,91 +344,25 @@ export const make: (options: { break } case "Summary": { - // Quantiles are always computed fresh from the sliding window - // They don't have temporality in the traditional sense - const dataPoints: Array = [{ - attributes: [...attributes, { key: "quantile", value: { stringValue: "min" } }], - startTimeUnixNano: intervalStartTime, - timeUnixNano: nowTime, - asDouble: state.state.min - }] - for (const [quantile, value] of state.state.quantiles) { - dataPoints.push({ - attributes: [...attributes, { key: "quantile", value: { stringValue: quantile.toString() } }], - startTimeUnixNano: intervalStartTime, - timeUnixNano: nowTime, - asDouble: value ?? 0 - }) - } - dataPoints.push({ - attributes: [...attributes, { key: "quantile", value: { stringValue: "max" } }], - startTimeUnixNano: intervalStartTime, - timeUnixNano: nowTime, - asDouble: state.state.max - }) - - let reportCount = state.state.count - let reportSum = state.state.sum - - if (isDelta) { - const previousState = previousSummaryState.get(metricKey) - if (previousState !== undefined) { - reportCount = state.state.count - previousState.count - reportSum = state.state.sum - previousState.sum - } - nextSummaryState.set(metricKey, { - count: state.state.count, - sum: state.state.sum - }) - } - - const countDataPoint: INumberDataPoint = { + // Count and sum are lifetime totals, so summaries are always cumulative + const dataPoint: ISummaryDataPoint = { attributes, - startTimeUnixNano: intervalStartTime, - timeUnixNano: nowTime, - asInt: reportCount - } - const sumDataPoint: INumberDataPoint = { - attributes, - startTimeUnixNano: intervalStartTime, + startTimeUnixNano: startTime, timeUnixNano: nowTime, - asDouble: reportSum + count: state.state.count, + sum: state.state.sum, + quantileValues: state.state.quantiles.flatMap(([quantile, value]) => + value === undefined ? [] : [{ quantile, value }] + ) } - - if (metricDataByName.has(`${state.id}_quantiles`)) { - metricDataByName.get(`${state.id}_quantiles`)!.sum!.dataPoints.push(...dataPoints) - metricDataByName.get(`${state.id}_count`)!.sum!.dataPoints.push(countDataPoint) - metricDataByName.get(`${state.id}_sum`)!.sum!.dataPoints.push(sumDataPoint) + if (metricDataByName.has(state.id)) { + metricDataByName.get(state.id)!.summary!.dataPoints.push(dataPoint) } else { addMetricData({ - name: `${state.id}_quantiles`, + name: state.id, description: state.description!, unit, - sum: { - aggregationTemporality: aggregationTemporalityEnum, - isMonotonic: false, - dataPoints - } - }) - addMetricData({ - name: `${state.id}_count`, - description: state.description!, - unit: "1", - sum: { - aggregationTemporality: aggregationTemporalityEnum, - isMonotonic: true, - dataPoints: [countDataPoint] - } - }) - addMetricData({ - name: `${state.id}_sum`, - description: state.description!, - unit: "1", - sum: { - aggregationTemporality: aggregationTemporalityEnum, - isMonotonic: true, - dataPoints: [sumDataPoint] - } + summary: { dataPoints: [dataPoint] } }) } break @@ -451,7 +385,6 @@ export const make: (options: { previousCounterState = nextCounterState previousHistogramState = nextHistogramState previousFrequencyState = nextFrequencyState - previousSummaryState = nextSummaryState previousExportTimeNanos = nowNanos committedSnapshotSequence = currentSnapshotSequence }) @@ -567,6 +500,20 @@ const makeMetricKey = (id: string, attributes: Metric.Metric.AttributeSet | unde return `${id}:${JSON.stringify(sortedEntries)}` } +const ucumUnits = new Map([ + ["nanoseconds", "ns"], + ["microseconds", "us"], + ["milliseconds", "ms"], + ["seconds", "s"], + ["bytes", "By"] +]) + +const toUcumUnit = (unit: string | undefined): string => unit === undefined ? "1" : ucumUnits.get(unit) ?? unit + +/** OTLP/JSON accepts int64 as a decimal string, which keeps values beyond 2^53 exact */ +const bigintToInt64 = (value: bigint): number | string => + Number.isSafeInteger(Number(value)) ? Number(value) : value.toString() + /** Previous state for histogram delta computation */ interface PreviousHistogramState { readonly count: number @@ -576,12 +523,6 @@ interface PreviousHistogramState { readonly max: number } -/** Previous state for summary delta computation */ -interface PreviousSummaryState { - readonly count: number - readonly sum: number -} - /** Properties of an InstrumentationScope. */ interface IInstrumentationScope { /** InstrumentationScope name */ @@ -675,7 +616,7 @@ interface INumberDataPoint { /** NumberDataPoint asDouble */ asDouble?: number | null /** NumberDataPoint asInt */ - asInt?: number + asInt?: number | string /** NumberDataPoint exemplars */ exemplars?: Array /** NumberDataPoint flags */ @@ -740,9 +681,9 @@ interface ISummaryDataPoint { /** SummaryDataPoint attributes */ attributes?: Array /** SummaryDataPoint startTimeUnixNano */ - startTimeUnixNano?: number + startTimeUnixNano?: Fixed64 /** SummaryDataPoint timeUnixNano */ - timeUnixNano?: string + timeUnixNano?: Fixed64 /** SummaryDataPoint count */ count?: number /** SummaryDataPoint sum */ diff --git a/repos/effect/packages/effect/src/observability/OtlpResource.ts b/packages/effect/src/observability/OtlpResource.ts similarity index 89% rename from repos/effect/packages/effect/src/observability/OtlpResource.ts rename to packages/effect/src/observability/OtlpResource.ts index 4cbe45595..22a44be97 100644 --- a/repos/effect/packages/effect/src/observability/OtlpResource.ts +++ b/packages/effect/src/observability/OtlpResource.ts @@ -13,6 +13,7 @@ import * as Config from "../Config.ts" import * as Effect from "../Effect.ts" import { format } from "../Formatter.ts" import * as Schema from "../Schema.ts" +import * as Version from "../Version.ts" /** * OTLP resource metadata attached to exported logs, metrics, and traces. @@ -28,46 +29,36 @@ export interface Resource { droppedAttributesCount: number } +const isBrowser = "window" in globalThis || "importScripts" in globalThis + /** * Creates an OTLP resource from service metadata and additional attributes. * * **Details** * - * The resource always includes `service.name`, includes `service.version` when - * provided, and converts custom attributes into OTLP attribute values. + * Adds `telemetry.sdk.*` defaults and converts custom attributes to OTLP values. + * Custom attributes override SDK defaults; service options override matching + * attributes. Omitted service options add no service attributes. * * @stability unstable * @category constructors * @since 4.0.0 */ export const make = (options: { - readonly serviceName: string + readonly serviceName?: string | undefined readonly serviceVersion?: string | undefined readonly attributes?: Record | undefined -}): Resource => { - const resourceAttributes = options.attributes - ? entriesToAttributes(Object.entries(options.attributes)) - : [] - resourceAttributes.push({ - key: "service.name", - value: { - stringValue: options.serviceName - } - }) - if (options.serviceVersion) { - resourceAttributes.push({ - key: "service.version", - value: { - stringValue: options.serviceVersion - } - }) - } - - return { - attributes: resourceAttributes, - droppedAttributesCount: 0 - } -} +}): Resource => ({ + attributes: entriesToAttributes(Object.entries({ + "telemetry.sdk.name": "effect", + "telemetry.sdk.language": isBrowser ? "webjs" : "nodejs", + "telemetry.sdk.version": Version.getCurrentVersion(), + ...options.attributes, + ...(options.serviceName ? { "service.name": options.serviceName } : undefined), + ...(options.serviceVersion ? { "service.version": options.serviceVersion } : undefined) + })), + droppedAttributesCount: 0 +}) /** * Creates an OTLP resource from explicit options and OpenTelemetry diff --git a/repos/effect/packages/effect/src/observability/OtlpSerialization.ts b/packages/effect/src/observability/OtlpSerialization.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/OtlpSerialization.ts rename to packages/effect/src/observability/OtlpSerialization.ts diff --git a/repos/effect/packages/effect/src/observability/OtlpTracer.ts b/packages/effect/src/observability/OtlpTracer.ts similarity index 92% rename from repos/effect/packages/effect/src/observability/OtlpTracer.ts rename to packages/effect/src/observability/OtlpTracer.ts index df341205d..c6f6197d5 100644 --- a/repos/effect/packages/effect/src/observability/OtlpTracer.ts +++ b/packages/effect/src/observability/OtlpTracer.ts @@ -26,6 +26,7 @@ import * as Option from "../Option.ts" import type * as Scope from "../Scope.ts" import * as Tracer from "../Tracer.ts" import type { ExtractTag } from "../Types.ts" +import * as Version from "../Version.ts" import * as OtlpEnv from "./internal/otlpEnv.ts" import * as Exporter from "./OtlpExporter.ts" import type { KeyValue, Resource } from "./OtlpResource.ts" @@ -67,7 +68,8 @@ export const make: ( const otelResource = yield* OtlpResource.fromConfig(options.resource) const serialization = yield* OtlpSerialization const scope: Scope = { - name: OtlpResource.serviceNameUnsafe(otelResource) + name: "effect", + version: Version.getCurrentVersion() } const exporter = yield* Exporter.make({ @@ -295,53 +297,44 @@ const makeOtlpSpan = (self: SpanImpl): OtlpSpan => { if (status.exit._tag === "Success") { otelStatus = { code: StatusCode.Ok } } else if (Cause.hasInterruptsOnly(status.exit.cause)) { - otelStatus = { - code: StatusCode.Ok, - message: "Interrupted" - } + otelStatus = { code: StatusCode.Unset } attributes.push({ - key: "span.label", - value: { stringValue: "⚠︎ Interrupted" } - }, { - key: "status.interrupted", + key: "effect.fiber.interrupted", value: { boolValue: true } }) } else { const errors = Cause.prettyErrors(status.exit.cause, { includeCauseInStack: true }) - otelStatus = { - code: StatusCode.Error - } - if (errors.length > 0) { - otelStatus.message = errors[0].message - for (const error of errors) { - events.push({ - name: "exception", - timeUnixNano: String(status.endTime), - droppedAttributesCount: 0, - attributes: [ - { - "key": "exception.type", - "value": { - "stringValue": error.name - } - }, - { - "key": "exception.message", - "value": { - "stringValue": error.message - } - }, - { - "key": "exception.stacktrace", - "value": { - "stringValue": error.stack ?? "No stack trace available" - } + otelStatus = errors.length > 0 + ? { code: StatusCode.Error, message: errors[0].message } + : { code: StatusCode.Ok } + for (const error of errors) { + events.push({ + name: "exception", + timeUnixNano: String(status.endTime), + droppedAttributesCount: 0, + attributes: [ + { + "key": "exception.type", + "value": { + "stringValue": error.name } - ] - }) - } + }, + { + "key": "exception.message", + "value": { + "stringValue": error.message + } + }, + { + "key": "exception.stacktrace", + "value": { + "stringValue": error.stack ?? "No stack trace available" + } + } + ] + }) } } @@ -413,6 +406,7 @@ export interface ScopeSpan { interface Scope { readonly name: string + readonly version?: string } interface OtlpSpan { diff --git a/repos/effect/packages/effect/src/observability/PrometheusMetrics.ts b/packages/effect/src/observability/PrometheusMetrics.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/PrometheusMetrics.ts rename to packages/effect/src/observability/PrometheusMetrics.ts diff --git a/repos/effect/packages/effect/src/observability/index.ts b/packages/effect/src/observability/index.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/index.ts rename to packages/effect/src/observability/index.ts diff --git a/repos/effect/packages/effect/src/observability/internal/otlpEnv.ts b/packages/effect/src/observability/internal/otlpEnv.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/internal/otlpEnv.ts rename to packages/effect/src/observability/internal/otlpEnv.ts diff --git a/repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts b/packages/effect/src/observability/internal/otlpProtobuf.ts similarity index 91% rename from repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts rename to packages/effect/src/observability/internal/otlpProtobuf.ts index a068668f9..038defb31 100644 --- a/repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts +++ b/packages/effect/src/observability/internal/otlpProtobuf.ts @@ -493,6 +493,62 @@ export const encodeHistogram = (histogram: { Proto.varintField(2, histogram.aggregationTemporality) ) +/** + * Encodes a SummaryDataPoint message. + * + * message SummaryDataPoint { + * repeated KeyValue attributes = 7; + * fixed64 start_time_unix_nano = 2; + * fixed64 time_unix_nano = 3; + * fixed64 count = 4; + * double sum = 5; + * repeated ValueAtQuantile quantile_values = 6; + * uint32 flags = 8; + * } + * + * message ValueAtQuantile { + * double quantile = 1; + * double value = 2; + * } + * + * @internal + */ +export const encodeSummaryDataPoint = (point: { + readonly attributes: ReadonlyArray + readonly startTimeUnixNano: string + readonly timeUnixNano: string + readonly count: string | number | bigint + readonly sum: number + readonly quantileValues: ReadonlyArray<{ readonly quantile: number; readonly value: number }> + readonly flags?: number | undefined +}): Uint8Array => + Proto.concat( + Proto.fixed64Field(2, BigInt(point.startTimeUnixNano)), + Proto.fixed64Field(3, BigInt(point.timeUnixNano)), + Proto.fixed64Field(4, BigInt(point.count)), + Proto.doubleField(5, point.sum), + Proto.repeatedField( + 6, + point.quantileValues, + (q) => Proto.concat(Proto.doubleField(1, q.quantile), Proto.doubleField(2, q.value)) + ), + Proto.repeatedField(7, point.attributes, encodeKeyValue), + point.flags !== undefined ? Proto.varintField(8, point.flags) : new Uint8Array(0) + ) + +/** + * Encodes a Summary message. + * + * message Summary { + * repeated SummaryDataPoint data_points = 1; + * } + * + * @internal + */ +export const encodeSummary = (summary: { + readonly dataPoints: ReadonlyArray[0]> +}): Uint8Array => Proto.repeatedField(1, summary.dataPoints, encodeSummaryDataPoint) + /** * Encodes a Metric message. * @@ -518,6 +574,7 @@ export const encodeMetric = (metric: { readonly gauge?: Parameters[0] | undefined readonly sum?: Parameters[0] | undefined readonly histogram?: Parameters[0] | undefined + readonly summary?: Parameters[0] | undefined }): Uint8Array => Proto.concat( Proto.stringField(1, metric.name), @@ -531,6 +588,9 @@ export const encodeMetric = (metric: { : new Uint8Array(0), metric.histogram !== undefined ? Proto.messageField(9, encodeHistogram(metric.histogram)) + : new Uint8Array(0), + metric.summary !== undefined + ? Proto.messageField(11, encodeSummary(metric.summary)) : new Uint8Array(0) ) diff --git a/repos/effect/packages/effect/src/observability/internal/protobuf.ts b/packages/effect/src/observability/internal/protobuf.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/internal/protobuf.ts rename to packages/effect/src/observability/internal/protobuf.ts diff --git a/repos/effect/packages/effect/src/persistence/KeyValueStore.ts b/packages/effect/src/persistence/KeyValueStore.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/KeyValueStore.ts rename to packages/effect/src/persistence/KeyValueStore.ts diff --git a/repos/effect/packages/effect/src/persistence/Persistable.ts b/packages/effect/src/persistence/Persistable.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Persistable.ts rename to packages/effect/src/persistence/Persistable.ts diff --git a/repos/effect/packages/effect/src/persistence/PersistedCache.ts b/packages/effect/src/persistence/PersistedCache.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/PersistedCache.ts rename to packages/effect/src/persistence/PersistedCache.ts diff --git a/repos/effect/packages/effect/src/persistence/PersistedQueue.ts b/packages/effect/src/persistence/PersistedQueue.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/PersistedQueue.ts rename to packages/effect/src/persistence/PersistedQueue.ts diff --git a/repos/effect/packages/effect/src/persistence/Persistence.ts b/packages/effect/src/persistence/Persistence.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Persistence.ts rename to packages/effect/src/persistence/Persistence.ts diff --git a/repos/effect/packages/effect/src/persistence/RateLimiter.ts b/packages/effect/src/persistence/RateLimiter.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/RateLimiter.ts rename to packages/effect/src/persistence/RateLimiter.ts diff --git a/repos/effect/packages/effect/src/persistence/Redis.ts b/packages/effect/src/persistence/Redis.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Redis.ts rename to packages/effect/src/persistence/Redis.ts diff --git a/repos/effect/packages/effect/src/persistence/index.ts b/packages/effect/src/persistence/index.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/index.ts rename to packages/effect/src/persistence/index.ts diff --git a/repos/effect/packages/effect/src/process/ChildProcess.ts b/packages/effect/src/process/ChildProcess.ts similarity index 100% rename from repos/effect/packages/effect/src/process/ChildProcess.ts rename to packages/effect/src/process/ChildProcess.ts diff --git a/repos/effect/packages/effect/src/process/ChildProcessSpawner.ts b/packages/effect/src/process/ChildProcessSpawner.ts similarity index 100% rename from repos/effect/packages/effect/src/process/ChildProcessSpawner.ts rename to packages/effect/src/process/ChildProcessSpawner.ts diff --git a/repos/effect/packages/effect/src/process/index.ts b/packages/effect/src/process/index.ts similarity index 100% rename from repos/effect/packages/effect/src/process/index.ts rename to packages/effect/src/process/index.ts diff --git a/repos/effect/packages/effect/src/reactivity/AsyncResult.ts b/packages/effect/src/reactivity/AsyncResult.ts similarity index 99% rename from repos/effect/packages/effect/src/reactivity/AsyncResult.ts rename to packages/effect/src/reactivity/AsyncResult.ts index 3a4a9673f..3869e5876 100644 --- a/repos/effect/packages/effect/src/reactivity/AsyncResult.ts +++ b/packages/effect/src/reactivity/AsyncResult.ts @@ -573,7 +573,7 @@ export const map: { */ export const flatMap: { ( - f: (a: A, prev: Success) => AsyncResult + f: (a: A, prev: Success) => AsyncResult ): (self: AsyncResult) => AsyncResult (self: AsyncResult, f: (a: A, prev: Success) => AsyncResult): AsyncResult } = dual( diff --git a/repos/effect/packages/effect/src/reactivity/Atom.ts b/packages/effect/src/reactivity/Atom.ts similarity index 99% rename from repos/effect/packages/effect/src/reactivity/Atom.ts rename to packages/effect/src/reactivity/Atom.ts index ecfbab214..c5fea04eb 100644 --- a/repos/effect/packages/effect/src/reactivity/Atom.ts +++ b/packages/effect/src/reactivity/Atom.ts @@ -712,7 +712,7 @@ export interface AtomRuntime extends Atom Effect.Effect, E, Scope.Scope | R | AtomRegistry | Reactivity.Reactivity>) - ) => Writable, A> + ) => Writable, A> } /** @@ -2168,18 +2168,21 @@ export const batch: (f: () => void) => void = Registry.batch // ----------------------------------------------------------------------------- /** - * Creates a browser-only signal atom that increments when the document becomes visible. + * Creates a signal atom that increments when the document becomes visible. * * **Details** * * It listens for `visibilitychange` events on `window` and removes the listener - * when the atom is disposed. + * when the atom is disposed. Without `window`, it stays at `0` and adds no listener. * * @stability unstable * @category constants * @since 4.0.0 */ export const windowFocusSignal: Atom = readable((get) => { + if (typeof window === "undefined") { + return 0 + } let count = 0 function update() { if (document.visibilityState === "visible") { @@ -2219,8 +2222,8 @@ export const makeRefreshOnSignal = <_>(signal: Atom<_>) => > * * **Details** * - * This helper is browser-only because `windowFocusSignal` depends on `window` and - * `document.visibilityState`. + * Without `window`, this helper does not trigger refreshes. Source atom updates + * are still forwarded. * * @stability unstable * @category combinators @@ -2385,7 +2388,8 @@ function updateSearchParams() { } } searchParamState.updates.clear() - const newUrl = `${window.location.pathname}?${searchParams.toString()}` + const search = searchParams.toString() + const newUrl = `${window.location.pathname}${search.length > 0 ? `?${search}` : ""}${window.location.hash}` window.history.pushState({}, "", newUrl) searchParamState.updating = false } diff --git a/repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts b/packages/effect/src/reactivity/AtomHttpApi.ts similarity index 98% rename from repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts rename to packages/effect/src/reactivity/AtomHttpApi.ts index ed10982fe..26be3e8e9 100644 --- a/repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts +++ b/packages/effect/src/reactivity/AtomHttpApi.ts @@ -57,7 +57,7 @@ export interface AtomHttpApiClient, EndpointIdentifier >, - const ResponseMode extends HttpApiEndpoint.ClientResponseMode = HttpApiEndpoint.ClientResponseMode + const ResponseMode extends HttpApiEndpoint.ClientResponseMode = "decoded-only" >( group: GroupIdentifier, endpoint: EndpointIdentifier, @@ -353,9 +353,7 @@ type ResponseByMode< Endpoint extends HttpApiEndpoint.Constraint, ResponseMode extends HttpApiEndpoint.ClientResponseMode > = HttpApiClient.Client.Response< - Effect.Success< - ReturnType, never, never>> - >, + HttpApiClient.SuccessType["~Success"]>, ResponseMode > diff --git a/repos/effect/packages/effect/src/reactivity/AtomRef.ts b/packages/effect/src/reactivity/AtomRef.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/AtomRef.ts rename to packages/effect/src/reactivity/AtomRef.ts diff --git a/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts b/packages/effect/src/reactivity/AtomRegistry.ts similarity index 99% rename from repos/effect/packages/effect/src/reactivity/AtomRegistry.ts rename to packages/effect/src/reactivity/AtomRegistry.ts index 91b1d0002..ef4af667c 100644 --- a/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts +++ b/packages/effect/src/reactivity/AtomRegistry.ts @@ -71,11 +71,13 @@ export interface AtomRegistry { readonly scheduler: Scheduler readonly schedulerAsync: Scheduler readonly getNodes: () => ReadonlyMap | string, Node> + readonly ensureNode: (atom: Atom.Atom) => Node readonly get: (atom: Atom.Atom) => A readonly mount: (atom: Atom.Atom) => () => void readonly refresh: (atom: Atom.Atom) => void readonly set: (atom: Atom.Writable, value: W) => void readonly setSerializable: (key: string, encoded: unknown) => void + readonly preloadedSerializable: Map readonly modify: (atom: Atom.Writable, f: (_: R) => [returnValue: A, nextValue: W]) => A readonly update: (atom: Atom.Writable, f: (_: R) => W) => void readonly subscribe: (atom: Atom.Atom, f: (_: A) => void, options?: { @@ -83,6 +85,7 @@ export interface AtomRegistry { }) => () => void readonly reset: () => void readonly dispose: () => void + scheduleNodeRemoval(node: Node): void onNodeAdded?: ((node: Node) => void) | undefined onNodeRemoved?: ((node: Node) => void) | undefined } @@ -102,10 +105,14 @@ export interface AtomRegistry { export interface Node { readonly atom: Atom.Atom readonly value: () => A + readonly valueOption: () => Option.Option + readonly canBeRemoved: boolean parents: Set> children: Set> listeners: Set<() => void> currentState(): "uninitialized" | "stale" | "valid" | "removed" + setInitialValue(value: A): void + subscribe(listener: () => void): () => void } /** diff --git a/repos/effect/packages/effect/src/reactivity/AtomRpc.ts b/packages/effect/src/reactivity/AtomRpc.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/AtomRpc.ts rename to packages/effect/src/reactivity/AtomRpc.ts diff --git a/repos/effect/packages/effect/src/reactivity/Hydration.ts b/packages/effect/src/reactivity/Hydration.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/Hydration.ts rename to packages/effect/src/reactivity/Hydration.ts diff --git a/repos/effect/packages/effect/src/reactivity/Reactivity.ts b/packages/effect/src/reactivity/Reactivity.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/Reactivity.ts rename to packages/effect/src/reactivity/Reactivity.ts diff --git a/repos/effect/packages/effect/src/reactivity/index.ts b/packages/effect/src/reactivity/index.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/index.ts rename to packages/effect/src/reactivity/index.ts diff --git a/repos/effect/packages/effect/src/rpc/Rpc.ts b/packages/effect/src/rpc/Rpc.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/Rpc.ts rename to packages/effect/src/rpc/Rpc.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcClient.ts b/packages/effect/src/rpc/RpcClient.ts similarity index 95% rename from repos/effect/packages/effect/src/rpc/RpcClient.ts rename to packages/effect/src/rpc/RpcClient.ts index 3272af43e..615d168ad 100644 --- a/repos/effect/packages/effect/src/rpc/RpcClient.ts +++ b/packages/effect/src/rpc/RpcClient.ts @@ -13,8 +13,9 @@ */ import type { NonEmptyReadonlyArray } from "../Array.ts" import * as Cause from "../Cause.ts" +import * as Clock from "../Clock.ts" import * as Context from "../Context.ts" -import type * as Duration from "../Duration.ts" +import * as Duration from "../Duration.ts" import * as Effect from "../Effect.ts" import * as Exit from "../Exit.ts" import * as Fiber from "../Fiber.ts" @@ -42,6 +43,7 @@ import type { Span } from "../Tracer.ts" import * as Transferable from "../workers/Transferable.ts" import * as Worker from "../workers/Worker.ts" import type { WorkerError } from "../workers/WorkerError.ts" +import * as RpcTracing from "./internal/tracing.ts" import * as Rpc from "./Rpc.ts" import { RpcClientDefect, RpcClientError } from "./RpcClientError.ts" import type * as RpcGroup from "./RpcGroup.ts" @@ -267,7 +269,7 @@ export const makeNoSerialization: requestIdCounter++ as RequestId) @@ -335,8 +337,8 @@ export const makeNoSerialization: | undefined /** * Runs for each retried `SocketOpenError` when `retryTransientErrors` is enabled. - * A missed pong fails in-flight calls and is not reported through this hook. + * A ping timeout fails in-flight calls and is not reported through this hook. * The returned `Effect` cannot fail with a typed error or require * services; defects are logged and ignored so retries can continue. */ @@ -1062,7 +1073,7 @@ export const makeProtocolSocket = (options?: { // `parser` is replaced on every connect, and a stateful serialization // encodes against the connection it is writing to, so the ping is encoded // when it is sent rather than once up front. - const pinger = yield* makePinger(Effect.suspend(() => writer.write(parser.encode(constPing)!))) + const pinger = yield* makePinger(Effect.suspend(() => writer.write(parser.encode(constPing)!)), options) let currentError: RpcClientError | undefined const broadcast = (response: FromServerEncoded) => @@ -1079,15 +1090,13 @@ export const makeProtocolSocket = (options?: { try { const responses = parser.decode(data) as Array if (responses.length === 0) return Effect.void + pinger.onFrame() let i = 0 return Effect.whileLoop({ while: () => i < responses.length, body: () => { const response = responses[i++] - if (response._tag === "Pong") { - pinger.onPong() - return Effect.void - } + if (response._tag === "Pong") return Effect.void if (Object.hasOwn(response, "requestId")) { const requestId = (response as FromServerEncoded & { readonly requestId: string | number }).requestId const clientId = requestClientMap.get(requestId) @@ -1202,43 +1211,58 @@ const defaultRetryPolicy = Schedule.min([ Schedule.spaced(5000) ]) -const makePinger = Effect.fnUntraced(function*(writePing: Effect.Effect) { - let recievedPong = true +const makePinger = Effect.fnUntraced(function*(writePing: Effect.Effect, options?: { + readonly pingInterval?: Duration.Input | undefined + readonly pingTimeout?: Duration.Input | undefined +}) { + const clock = yield* Clock.Clock + const interval = options?.pingInterval ?? "5 seconds" + const timeoutMillis = Duration.toMillis(options?.pingTimeout ?? interval) + let lastSeen = clock.currentTimeMillisUnsafe() const latch = Latch.makeUnsafe() + const onFrame = () => { + lastSeen = clock.currentTimeMillisUnsafe() + } const reset = () => { - recievedPong = true + onFrame() latch.closeUnsafe() } - const onPong = () => { - recievedPong = true - } yield* Effect.suspend((): Effect.Effect => { - if (!recievedPong) return latch.open - recievedPong = false + if (clock.currentTimeMillisUnsafe() - lastSeen > timeoutMillis) return latch.open return writePing }).pipe( - Effect.delay("5 seconds"), + Effect.delay(interval), Effect.ignore, Effect.forever, Effect.interruptible, Effect.forkScoped ) - return { timeout: latch.await, reset, onPong } as const + return { timeout: latch.await, reset, onFrame } as const }) /** * Provides a client `Protocol` backed by the current `Socket` and * `RpcSerialization` services. * + * **Details** + * + * `pingInterval` defaults to 5 seconds. `pingTimeout` defaults to the interval + * and measures time since the last decoded server frame, not the last pong. + * The timeout is checked on each ping tick; any decoded frame counts as liveness. + * `retryPolicy` configures retries after socket errors. + * * @stability unstable * @category layers * @since 4.0.0 */ export const layerProtocolSocket = (options?: { + readonly pingInterval?: Duration.Input | undefined + readonly pingTimeout?: Duration.Input | undefined readonly retryTransientErrors?: boolean | undefined + readonly retryPolicy?: Schedule.Schedule | undefined /** * Runs for each retried `SocketOpenError` when `retryTransientErrors` is enabled. - * A missed pong fails in-flight calls and is not reported through this hook. + * A ping timeout fails in-flight calls and is not reported through this hook. * The returned `Effect` cannot fail with a typed error or require * services; defects are logged and ignored so retries can continue. */ diff --git a/repos/effect/packages/effect/src/rpc/RpcClientError.ts b/packages/effect/src/rpc/RpcClientError.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcClientError.ts rename to packages/effect/src/rpc/RpcClientError.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcGroup.ts b/packages/effect/src/rpc/RpcGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcGroup.ts rename to packages/effect/src/rpc/RpcGroup.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcMessage.ts b/packages/effect/src/rpc/RpcMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcMessage.ts rename to packages/effect/src/rpc/RpcMessage.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcMiddleware.ts b/packages/effect/src/rpc/RpcMiddleware.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcMiddleware.ts rename to packages/effect/src/rpc/RpcMiddleware.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcSchema.ts b/packages/effect/src/rpc/RpcSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcSchema.ts rename to packages/effect/src/rpc/RpcSchema.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcSerialization.ts b/packages/effect/src/rpc/RpcSerialization.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcSerialization.ts rename to packages/effect/src/rpc/RpcSerialization.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcServer.ts b/packages/effect/src/rpc/RpcServer.ts similarity index 97% rename from repos/effect/packages/effect/src/rpc/RpcServer.ts rename to packages/effect/src/rpc/RpcServer.ts index a6d06f709..bda39256d 100644 --- a/repos/effect/packages/effect/src/rpc/RpcServer.ts +++ b/packages/effect/src/rpc/RpcServer.ts @@ -44,6 +44,7 @@ import type * as Types from "../Types.ts" import * as Transferable from "../workers/Transferable.ts" import type { WorkerError } from "../workers/WorkerError.ts" import * as WorkerRunner from "../workers/WorkerRunner.ts" +import * as RpcTracing from "./internal/tracing.ts" import * as Rpc from "./Rpc.ts" import type * as RpcGroup from "./RpcGroup.ts" import type { @@ -117,7 +118,7 @@ export const makeNoSerialization: ( const enableTracing = options.disableTracing !== true const enableSpanPropagation = options.disableSpanPropagation !== true const supportsAck = options.disableClientAcks !== true - const spanPrefix = options.spanPrefix ?? "RpcServer" + const spanPrefix = options.spanPrefix const concurrency = options.concurrency ?? "unbounded" const disableFatalDefects = options.disableFatalDefects ?? false const services = yield* Effect.context | Scope.Scope>() @@ -331,9 +332,10 @@ export const makeNoSerialization: ( requestFiber.context, Tracer.ParentSpan ) - effect = Effect.withSpan(effect, `${spanPrefix}.${request.tag}`, { + effect = Effect.withSpan(effect, RpcTracing.spanName(spanPrefix, request.tag), { captureStackTrace: false, - attributes: options.spanAttributes, + kind: "server", + attributes: RpcTracing.spanAttributes(request.tag, options.spanAttributes), parent: enableSpanPropagation && request.spanId ? Tracer.externalSpan({ traceId: request.traceId!, @@ -359,11 +361,14 @@ export const makeNoSerialization: ( ) const fiber = trackFiber( runFork( - effect, + // Register before the handler runs to catch synchronous cancellation. + Effect.withFiber((fiber) => { + client.fibers.set(request.id, fiber) + return effect + }), isUninterruptible ? { uninterruptible: true } : undefined ) ) - client.fibers.set(request.id, fiber) fiber.addObserver(function onExit(exit: Exit.Exit): void { if (deferred) { const fiber = trackFiber(runFork(Effect.onExit(Deferred.await(deferred), (exit) => @@ -564,6 +569,10 @@ export const make: ( supportsTransferables } = yield* Protocol const encodeDefectUnsafe = Schema.encodeSync(codecFor(Schema.Defect())) + // Unknown tags have no RPC schema, so use a defect-only exit schema. + const encodeUnknownRequestExit: Schemas["encodeExit"] = Schema.encodeUnknownEffect( + codecFor(Schema.Exit(Schema.Never, Schema.Never, Schema.Defect())) + ) as any const services = yield* Effect.context | Rpc.Middleware>() const scope = yield* Scope.make() @@ -629,7 +638,6 @@ export const make: ( u: NonEmptyReadonlyArray ) => Effect.Effect, Schema.SchemaError> readonly encodeExit: (u: unknown) => Effect.Effect - readonly encodeDefect: (u: unknown) => Effect.Effect readonly context: Context.Context readonly collector?: Transferable.Collector["Service"] | undefined } @@ -648,7 +656,6 @@ export const make: ( ) ) as any, encodeExit: Schema.encodeUnknownEffect(codecFor(Rpc.exitSchema(rpc as any))) as any, - encodeDefect: Schema.encodeUnknownEffect(codecFor(rpc.defectSchema)) as any, context: entry.context } schemasCache.set(rpc, schemas) @@ -694,7 +701,7 @@ export const make: ( client.schemas.delete(requestId) const defect = Cause.squash(Cause.map(cause, (e) => SchemaIssue.defaultFormatter(e.issue))) return Effect.andThen( - sendRequestDefect(client, requestId, schemas.encodeDefect, defect), + sendRequestDefect(client, requestId, schemas.encodeExit, defect), server.write(client.id, { _tag: "Interrupt", requestId, interruptors: [] }) ) }) @@ -703,22 +710,14 @@ export const make: ( const sendRequestDefect = ( client: Client, requestId: RequestId, - encodeDefect: (u: unknown) => Effect.Effect, + encodeExit: Schemas["encodeExit"], defect: unknown ) => Effect.catchCause( - Effect.flatMap(encodeDefect(defect), (encodedDefect) => - send(client.id, { - _tag: "Exit", - requestId, - exit: { - _tag: "Failure", - cause: [{ - _tag: "Die", - defect: encodedDefect - }] - } - })), + Effect.flatMap( + encodeExit(Exit.die(defect)), + (exit) => send(client.id, responseEnvelope(requestId, "Exit", exit)) + ), (cause) => sendDefect(client, Cause.squash(cause)) ) @@ -784,7 +783,7 @@ export const make: ( } const rpc = group.requests.get(tag) if (!rpc) { - return sendRequestDefect(client, requestId, (defect) => Effect.succeed(defect), `Unknown request tag: ${tag}`) + return sendRequestDefect(client, requestId, encodeUnknownRequestExit, `Unknown request tag: ${tag}`) } const schemas = getSchemas(rpc as any) const decoded = schemas.decode(request.payload) @@ -798,7 +797,7 @@ export const make: ( Effect.provideContext(decoded, schemas.context), { onFailure: (error) => - sendRequestDefect(client, requestId, schemas.encodeDefect, SchemaIssue.defaultFormatter(error.issue)), + sendRequestDefect(client, requestId, schemas.encodeExit, SchemaIssue.defaultFormatter(error.issue)), onSuccess: (payload) => writeDecodedRequest(client, requestId, schemas, request, payload) } ) @@ -1181,7 +1180,10 @@ export const makeProtocolWithHttpEffect: ( }) } - const initialChunk = yield* Queue.takeAll(queue) as any as Effect.Effect> + const initialChunk = yield* Pull.catchDone( + Queue.takeAll(queue as Queue.Dequeue), + () => Effect.succeed([]) + ) if (queue.state._tag === "Done") { return HttpServerResponse.uint8Array(mergeUint8Arrays(initialChunk), { contentType: serialization.contentType diff --git a/repos/effect/packages/effect/src/rpc/RpcTest.ts b/packages/effect/src/rpc/RpcTest.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcTest.ts rename to packages/effect/src/rpc/RpcTest.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcWorker.ts b/packages/effect/src/rpc/RpcWorker.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcWorker.ts rename to packages/effect/src/rpc/RpcWorker.ts diff --git a/repos/effect/packages/effect/src/rpc/Utils.ts b/packages/effect/src/rpc/Utils.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/Utils.ts rename to packages/effect/src/rpc/Utils.ts diff --git a/repos/effect/packages/effect/src/rpc/index.ts b/packages/effect/src/rpc/index.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/index.ts rename to packages/effect/src/rpc/index.ts diff --git a/packages/effect/src/rpc/internal/tracing.ts b/packages/effect/src/rpc/internal/tracing.ts new file mode 100644 index 000000000..4911da29e --- /dev/null +++ b/packages/effect/src/rpc/internal/tracing.ts @@ -0,0 +1,22 @@ +/** + * Names the span after the RPC method, or `${spanPrefix}.${tag}` when a prefix + * is configured. + * + * @internal + */ +export const spanName = (spanPrefix: string | undefined, tag: string): string => + spanPrefix === undefined ? tag : `${spanPrefix}.${tag}` + +/** + * Default RPC span attributes, overridden by user `spanAttributes`. + * + * @internal + */ +export const spanAttributes = ( + tag: string, + attributes: Record | undefined +): Record => ({ + "rpc.system.name": "effect_rpc", + "rpc.method": tag, + ...attributes +}) diff --git a/repos/effect/packages/effect/src/schema/Model.ts b/packages/effect/src/schema/Model.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/Model.ts rename to packages/effect/src/schema/Model.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaAOTCompiler.ts b/packages/effect/src/schema/SchemaAOTCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaAOTCompiler.ts rename to packages/effect/src/schema/SchemaAOTCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaAOTCompiler/Build.ts b/packages/effect/src/schema/SchemaAOTCompiler/Build.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaAOTCompiler/Build.ts rename to packages/effect/src/schema/SchemaAOTCompiler/Build.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaCompiler.ts b/packages/effect/src/schema/SchemaCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaCompiler.ts rename to packages/effect/src/schema/SchemaCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts b/packages/effect/src/schema/SchemaCompiler/runtime.ts similarity index 97% rename from repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts rename to packages/effect/src/schema/SchemaCompiler/runtime.ts index 9ce8e2d7c..d2985054b 100644 --- a/repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts +++ b/packages/effect/src/schema/SchemaCompiler/runtime.ts @@ -65,12 +65,15 @@ const makeObjectBase = ( const runFallback: SchemaIssueParser = (input, options) => (fallback ??= ast.getParser(compile, compileField))(input, options) const resume = ( - state: ObjectParserState, + snapshot: ObjectParserState, index: number, pending: Effect.Effect ): Effect.Effect => { const property = properties![index] return Effect.flatMap(Effect.exit(pending), (exit) => { + // Each execution gets its own output; `issues` is still undefined here + // because `errors: "all"` is routed to the fallback before suspending. + const state: ObjectParserState = { ...snapshot, out: { ...snapshot.out } } const terminal = SchemaAST.stepProperty(state, property, exit) if (terminal) return terminal const done = () => InternalParser.succeed(state.out) diff --git a/repos/effect/packages/effect/src/schema/SchemaJITCompiler.ts b/packages/effect/src/schema/SchemaJITCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaJITCompiler.ts rename to packages/effect/src/schema/SchemaJITCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaJITCompiler/enable.ts b/packages/effect/src/schema/SchemaJITCompiler/enable.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaJITCompiler/enable.ts rename to packages/effect/src/schema/SchemaJITCompiler/enable.ts diff --git a/repos/effect/packages/effect/src/schema/VariantSchema.ts b/packages/effect/src/schema/VariantSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/VariantSchema.ts rename to packages/effect/src/schema/VariantSchema.ts diff --git a/repos/effect/packages/effect/src/schema/index.ts b/packages/effect/src/schema/index.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/index.ts rename to packages/effect/src/schema/index.ts diff --git a/repos/effect/packages/effect/src/socket/Socket.ts b/packages/effect/src/socket/Socket.ts similarity index 100% rename from repos/effect/packages/effect/src/socket/Socket.ts rename to packages/effect/src/socket/Socket.ts diff --git a/repos/effect/packages/effect/src/socket/SocketServer.ts b/packages/effect/src/socket/SocketServer.ts similarity index 100% rename from repos/effect/packages/effect/src/socket/SocketServer.ts rename to packages/effect/src/socket/SocketServer.ts diff --git a/repos/effect/packages/effect/src/socket/index.ts b/packages/effect/src/socket/index.ts similarity index 100% rename from repos/effect/packages/effect/src/socket/index.ts rename to packages/effect/src/socket/index.ts diff --git a/repos/effect/packages/effect/src/sql/Migrator.ts b/packages/effect/src/sql/Migrator.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/Migrator.ts rename to packages/effect/src/sql/Migrator.ts diff --git a/repos/effect/packages/effect/src/sql/SqlClient.ts b/packages/effect/src/sql/SqlClient.ts similarity index 98% rename from repos/effect/packages/effect/src/sql/SqlClient.ts rename to packages/effect/src/sql/SqlClient.ts index 3c71ebed9..61d39e97b 100644 --- a/repos/effect/packages/effect/src/sql/SqlClient.ts +++ b/packages/effect/src/sql/SqlClient.ts @@ -394,7 +394,7 @@ export const makeWithTransaction = (options: { Effect.uninterruptibleMask((restore) => Effect.useSpan( "sql.transaction", - { kind: "client" }, + { kind: "internal" }, (span) => Effect.withFiber((fiber) => { for (const [key, value] of options.spanAttributes) { @@ -427,18 +427,18 @@ export const makeWithTransaction = (options: { let effect: Effect.Effect if (Exit.isSuccess(exit)) { if (id === 0) { - span.event("db.transaction.commit", clock.currentTimeNanosUnsafe()) + span.event("effect.sql.transaction.commit", clock.currentTimeNanosUnsafe()) const onCommitFailure = options.onCommitFailure effect = Effect.orDie(options.commit(conn)) if (onCommitFailure) { effect = Effect.onError(effect, () => Effect.orDie(onCommitFailure(conn))) } } else { - span.event("db.transaction.savepoint", clock.currentTimeNanosUnsafe()) + span.event("effect.sql.transaction.savepoint", clock.currentTimeNanosUnsafe()) effect = Effect.void } } else { - span.event("db.transaction.rollback", clock.currentTimeNanosUnsafe()) + span.event("effect.sql.transaction.rollback", clock.currentTimeNanosUnsafe()) effect = Effect.orDie( id > 0 ? options.rollbackSavepoint(conn, id) diff --git a/repos/effect/packages/effect/src/sql/SqlConnection.ts b/packages/effect/src/sql/SqlConnection.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlConnection.ts rename to packages/effect/src/sql/SqlConnection.ts diff --git a/repos/effect/packages/effect/src/sql/SqlError.ts b/packages/effect/src/sql/SqlError.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlError.ts rename to packages/effect/src/sql/SqlError.ts diff --git a/repos/effect/packages/effect/src/sql/SqlModel.ts b/packages/effect/src/sql/SqlModel.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlModel.ts rename to packages/effect/src/sql/SqlModel.ts diff --git a/repos/effect/packages/effect/src/sql/SqlResolver.ts b/packages/effect/src/sql/SqlResolver.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlResolver.ts rename to packages/effect/src/sql/SqlResolver.ts diff --git a/repos/effect/packages/effect/src/sql/SqlSchema.ts b/packages/effect/src/sql/SqlSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlSchema.ts rename to packages/effect/src/sql/SqlSchema.ts diff --git a/repos/effect/packages/effect/src/sql/SqlStream.ts b/packages/effect/src/sql/SqlStream.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlStream.ts rename to packages/effect/src/sql/SqlStream.ts diff --git a/repos/effect/packages/effect/src/sql/Statement.ts b/packages/effect/src/sql/Statement.ts similarity index 95% rename from repos/effect/packages/effect/src/sql/Statement.ts rename to packages/effect/src/sql/Statement.ts index f09348f6e..3829bc013 100644 --- a/repos/effect/packages/effect/src/sql/Statement.ts +++ b/packages/effect/src/sql/Statement.ts @@ -114,7 +114,7 @@ export const CurrentTransformer = Context.Reference("ef }) /** - * Parents driver spans under `sql.execute` for every client in the current scope, + * Parents driver spans under the statement span for every client in the current scope, * including acquisition and stream pulls. Defaults to `false`; ignored when tracing is disabled. * * @stability unstable @@ -1269,10 +1269,62 @@ export const defaultTransforms = ( } as const } +/** + * Returns the OpenTelemetry database span target for a client with the given + * span attributes: `db.namespace`, then `server.address[:server.port]`, or + * `undefined` when neither is set. + * + * @stability unstable + * @category tracing + * @since 4.0.1 + */ +export const spanTarget = (spanAttributes: ReadonlyArray): string | undefined => { + const namespace = spanAttribute(spanAttributes, "db.namespace") + if (isNonEmptyString(namespace)) { + return namespace + } + const address = spanAttribute(spanAttributes, "server.address") + if (isNonEmptyString(address)) { + const port = spanAttribute(spanAttributes, "server.port") + return port === undefined ? address : `${address}:${port}` + } + return undefined +} + +/** + * Returns the span name for statements run by a client with the given span + * attributes, following the OpenTelemetry database span conventions: the + * {@link spanTarget}, then `db.system.name`, falling back to `sql.execute`. + * + * @stability unstable + * @category tracing + * @since 4.0.1 + */ +export const spanName = (spanAttributes: ReadonlyArray): string => { + const target = spanTarget(spanAttributes) + if (target !== undefined) { + return target + } + const system = spanAttribute(spanAttributes, "db.system.name") + return isNonEmptyString(system) ? system : "sql.execute" +} + // internal -const ATTR_DB_OPERATION_NAME = "db.operation.name" const ATTR_DB_QUERY_TEXT = "db.query.text" +const ATTR_SQL_METHOD = "effect.sql.method" + +// the last value wins, matching the order the attributes are applied to spans +const spanAttribute = (spanAttributes: ReadonlyArray, key: string): unknown => { + for (let i = spanAttributes.length - 1; i >= 0; i--) { + if (spanAttributes[i][0] === key) { + return spanAttributes[i][1] + } + } + return undefined +} + +const isNonEmptyString = (u: unknown): u is string => typeof u === "string" && u.length > 0 interface StatementImpl extends Statement { readonly segments: ReadonlyArray @@ -1338,7 +1390,7 @@ const StatementProto: Omit< withoutTransform = false ): Effect.Effect { return Effect.useSpan( - "sql.execute", + spanName(this.spanAttributes), { kind: "client" }, (span) => this.withConnectionSpan( @@ -1365,7 +1417,7 @@ const StatementProto: Omit< for (const [key, value] of this.spanAttributes) { span.attribute(key, value) } - span.attribute(ATTR_DB_OPERATION_NAME, operation) + span.attribute(ATTR_SQL_METHOD, operation) span.attribute(ATTR_DB_QUERY_TEXT, sql) const execute = this.borrower === undefined ? Effect.scoped(Effect.flatMap(this.acquirer, (_) => f(_, sql, params))) @@ -1395,14 +1447,14 @@ const StatementProto: Omit< get stream(): Stream.Stream { const self = this as StatementImpl return Stream.unwrap(Effect.flatMap( - Effect.makeSpanScoped("sql.execute", { kind: "client" }), + Effect.makeSpanScoped(spanName(self.spanAttributes), { kind: "client" }), (span) => withStatement(self, span, (statement, fiber) => { const [sql, params] = statement.compile() for (const [key, value] of self.spanAttributes) { span.attribute(key, value) } - span.attribute(ATTR_DB_OPERATION_NAME, "executeStream") + span.attribute(ATTR_SQL_METHOD, "executeStream") span.attribute(ATTR_DB_QUERY_TEXT, sql) const acquire = Effect.map(self.acquirer, (_) => _.executeStream(sql, params, self.transformRows)) return fiber.cache.tracerEnabled && fiber.getRef(SpanPropagationEnabled) @@ -1440,7 +1492,7 @@ const StatementProto: Omit< ...Effectable.Prototype>({ label: "Statement", evaluate(fiber) { - const span = internalEffect.makeSpanUnsafe(fiber, "sql.execute", { kind: "client" }) + const span = internalEffect.makeSpanUnsafe(fiber, spanName(this.spanAttributes), { kind: "client" }) const clock = fiber.getRef(Clock) const timingEnabled = fiber.getRef(TracerTimingEnabled) return Effect.onExit( diff --git a/repos/effect/packages/effect/src/sql/index.ts b/packages/effect/src/sql/index.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/index.ts rename to packages/effect/src/sql/index.ts diff --git a/repos/effect/packages/effect/src/testing/TestClock.ts b/packages/effect/src/testing/TestClock.ts similarity index 98% rename from repos/effect/packages/effect/src/testing/TestClock.ts rename to packages/effect/src/testing/TestClock.ts index 394177642..d4a4951c8 100644 --- a/repos/effect/packages/effect/src/testing/TestClock.ts +++ b/packages/effect/src/testing/TestClock.ts @@ -8,6 +8,7 @@ * live clock, and warning when a test appears to be waiting on time without * advancing it. * + * @stability stable * @since 2.0.0 */ import * as Arr from "../Array.ts" @@ -88,6 +89,7 @@ import * as Semaphore from "../Semaphore.ts" * await Effect.runPromise(Effect.provide(program, TestClock.layer())) * ``` * + * @stability stable * @category models * @since 2.0.0 */ @@ -133,6 +135,7 @@ export interface TestClock extends Clock.Clock { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @since 2.0.0 */ export declare namespace TestClock { @@ -238,6 +241,7 @@ const millisToNanos = (millis: number): bigint => { * await Effect.runPromise(Effect.scoped(program)) * ``` * + * @stability stable * @category constructors * @since 4.0.0 */ @@ -430,6 +434,7 @@ export const make = Effect.fnUntraced(function*( * await Effect.runPromise(Effect.provide(program, customTestClockLayer)) // => 3_600_000 * ``` * + * @stability stable * @category layers * @since 4.0.0 */ @@ -463,6 +468,7 @@ export const layer: (options?: TestClock.Options) => Layer.Layer = fl * await Effect.runPromise(Effect.provide(program, TestClock.layer())) * ``` * + * @stability stable * @category testing * @since 2.0.0 */ @@ -501,6 +507,7 @@ export const testClockWith = ( * await Effect.runPromise(Effect.provide(program, TestClock.layer())) * ``` * + * @stability stable * @category testing * @since 2.0.0 */ @@ -538,6 +545,7 @@ export const adjust = (duration: Duration.Input): Effect.Effect => * await Effect.runPromise(Effect.provide(program, TestClock.layer())) * ``` * + * @stability stable * @category testing * @since 2.0.0 */ @@ -574,6 +582,7 @@ export const setTime = (timestamp: number): Effect.Effect => * await Effect.runPromise(Effect.provide(program, TestClock.layer())) * ``` * + * @stability stable * @category testing * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/testing/TestConsole.ts b/packages/effect/src/testing/TestConsole.ts similarity index 98% rename from repos/effect/packages/effect/src/testing/TestConsole.ts rename to packages/effect/src/testing/TestConsole.ts index b8a5a52f6..62fbe1a81 100644 --- a/repos/effect/packages/effect/src/testing/TestConsole.ts +++ b/packages/effect/src/testing/TestConsole.ts @@ -7,6 +7,7 @@ * console layer, helpers for reading captured `Console.log` and `Console.error` * arguments, and access to the provided test console service. * + * @stability unstable * @since 4.0.0 */ import * as Array from "../Array.ts" @@ -53,6 +54,7 @@ import * as Layer from "../Layer.ts" * @see {@link logLines} for reading captured `Console.log` calls * @see {@link errorLines} for reading captured `Console.error` calls * + * @stability unstable * @category models * @since 4.0.0 */ @@ -87,6 +89,7 @@ export interface TestConsole extends Console.Console { * * Use when referring to types nested under the `TestConsole` namespace. * + * @stability unstable * @since 4.0.0 */ export declare namespace TestConsole { @@ -173,6 +176,7 @@ export declare namespace TestConsole { * * @see {@link layer} for providing a `TestConsole` as a `Layer` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -250,6 +254,7 @@ export const make = Effect.gen(function*() { * @see {@link logLines} for reading captured `Console.log` calls directly * @see {@link errorLines} for reading captured `Console.error` calls directly * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -288,6 +293,7 @@ export const testConsoleWith = (f: (console: TestConsole) => Effect.Eff * @see {@link make} for constructing the service value directly * @see {@link testConsoleWith} for accessing the provided test console service * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -324,6 +330,7 @@ export const layer: Layer.Layer = Layer.effect(Console.Console)(mak * @see {@link errorLines} for reading captured `Console.error` output * @see {@link layer} for capturing console calls during a test * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -362,6 +369,7 @@ export const logLines: Effect.Effect, never, never> = tes * @see {@link logLines} for reading captured `Console.log` output * @see {@link layer} for capturing console calls during a test * + * @stability unstable * @category testing * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/testing/TestSchema.ts b/packages/effect/src/testing/TestSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/testing/TestSchema.ts rename to packages/effect/src/testing/TestSchema.ts diff --git a/repos/effect/packages/effect/src/testing/index.ts b/packages/effect/src/testing/index.ts similarity index 82% rename from repos/effect/packages/effect/src/testing/index.ts rename to packages/effect/src/testing/index.ts index aca10dddf..12aca49b5 100644 --- a/repos/effect/packages/effect/src/testing/index.ts +++ b/packages/effect/src/testing/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability stable * @since 2.0.0 */ export * as TestClock from "./TestClock.ts" /** + * @stability unstable * @since 4.0.0 */ export * as TestConsole from "./TestConsole.ts" diff --git a/repos/effect/packages/effect/src/workers/Transferable.ts b/packages/effect/src/workers/Transferable.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/Transferable.ts rename to packages/effect/src/workers/Transferable.ts diff --git a/repos/effect/packages/effect/src/workers/Worker.ts b/packages/effect/src/workers/Worker.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/Worker.ts rename to packages/effect/src/workers/Worker.ts diff --git a/repos/effect/packages/effect/src/workers/WorkerError.ts b/packages/effect/src/workers/WorkerError.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/WorkerError.ts rename to packages/effect/src/workers/WorkerError.ts diff --git a/repos/effect/packages/effect/src/workers/WorkerRunner.ts b/packages/effect/src/workers/WorkerRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/WorkerRunner.ts rename to packages/effect/src/workers/WorkerRunner.ts diff --git a/repos/effect/packages/effect/src/workers/index.ts b/packages/effect/src/workers/index.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/index.ts rename to packages/effect/src/workers/index.ts diff --git a/repos/effect/packages/effect/src/workflow/Activity.ts b/packages/effect/src/workflow/Activity.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/Activity.ts rename to packages/effect/src/workflow/Activity.ts diff --git a/repos/effect/packages/effect/src/workflow/DurableClock.ts b/packages/effect/src/workflow/DurableClock.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/DurableClock.ts rename to packages/effect/src/workflow/DurableClock.ts diff --git a/repos/effect/packages/effect/src/workflow/DurableDeferred.ts b/packages/effect/src/workflow/DurableDeferred.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/DurableDeferred.ts rename to packages/effect/src/workflow/DurableDeferred.ts diff --git a/repos/effect/packages/effect/src/workflow/DurableQueue.ts b/packages/effect/src/workflow/DurableQueue.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/DurableQueue.ts rename to packages/effect/src/workflow/DurableQueue.ts diff --git a/repos/effect/packages/effect/src/workflow/Workflow.ts b/packages/effect/src/workflow/Workflow.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/Workflow.ts rename to packages/effect/src/workflow/Workflow.ts diff --git a/repos/effect/packages/effect/src/workflow/WorkflowEngine.ts b/packages/effect/src/workflow/WorkflowEngine.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/WorkflowEngine.ts rename to packages/effect/src/workflow/WorkflowEngine.ts diff --git a/repos/effect/packages/effect/src/workflow/WorkflowProxy.ts b/packages/effect/src/workflow/WorkflowProxy.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/WorkflowProxy.ts rename to packages/effect/src/workflow/WorkflowProxy.ts diff --git a/repos/effect/packages/effect/src/workflow/WorkflowProxyServer.ts b/packages/effect/src/workflow/WorkflowProxyServer.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/WorkflowProxyServer.ts rename to packages/effect/src/workflow/WorkflowProxyServer.ts diff --git a/repos/effect/packages/effect/src/workflow/index.ts b/packages/effect/src/workflow/index.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/index.ts rename to packages/effect/src/workflow/index.ts diff --git a/repos/effect/packages/effect/src/workflow/internal/crypto.ts b/packages/effect/src/workflow/internal/crypto.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/internal/crypto.ts rename to packages/effect/src/workflow/internal/crypto.ts diff --git a/repos/effect/packages/effect/test/Array.test.ts b/packages/effect/test/Array.test.ts similarity index 98% rename from repos/effect/packages/effect/test/Array.test.ts rename to packages/effect/test/Array.test.ts index 0c939d3d3..7021b62ef 100644 --- a/repos/effect/packages/effect/test/Array.test.ts +++ b/packages/effect/test/Array.test.ts @@ -1593,6 +1593,31 @@ describe("Array", () => { deepStrictEqual(reducer.initialValue, []) }) + it("makeReducerConcat does not share mutable empty results between calls or reducers", () => { + const reducer = Arr.makeReducerConcat() + const xs = reducer.combineAll([]) + try { + xs.push(99) + deepStrictEqual(reducer.combineAll([]), []) + deepStrictEqual(reducer.combineAll([[1]]), [1]) + deepStrictEqual(Arr.makeReducerConcat().combineAll([["ok"]]), ["ok"]) + } finally { + // Restore the mutated result so a failing regression does not affect other tests. + xs.length = 0 + } + }) + + it("getReadonlyReducerConcat isolates identities and empty results", () => { + const reducer = Arr.getReadonlyReducerConcat() + const first = reducer.combineAll([]) + const second = reducer.combineAll([]) + strictEqual(reducer.initialValue === Arr.getReadonlyReducerConcat().initialValue, false) + strictEqual(reducer.initialValue === Arr.makeReducerConcat().initialValue, false) + strictEqual(first === second, false) + strictEqual(first === reducer.initialValue, false) + strictEqual(second === reducer.initialValue, false) + }) + it("allocate", () => { deepStrictEqual(Arr.allocate(0).length, 0) deepStrictEqual(Arr.allocate(Number.NaN).length, 0) diff --git a/repos/effect/packages/effect/test/ArrayNaNIndex.test.ts b/packages/effect/test/ArrayNaNIndex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ArrayNaNIndex.test.ts rename to packages/effect/test/ArrayNaNIndex.test.ts diff --git a/repos/effect/packages/effect/test/AtomRef.test.ts b/packages/effect/test/AtomRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/AtomRef.test.ts rename to packages/effect/test/AtomRef.test.ts diff --git a/repos/effect/packages/effect/test/BigDecimal.test.ts b/packages/effect/test/BigDecimal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/BigDecimal.test.ts rename to packages/effect/test/BigDecimal.test.ts diff --git a/repos/effect/packages/effect/test/BigInt.test.ts b/packages/effect/test/BigInt.test.ts similarity index 100% rename from repos/effect/packages/effect/test/BigInt.test.ts rename to packages/effect/test/BigInt.test.ts diff --git a/repos/effect/packages/effect/test/Boolean.test.ts b/packages/effect/test/Boolean.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Boolean.test.ts rename to packages/effect/test/Boolean.test.ts diff --git a/repos/effect/packages/effect/test/Brand.test.ts b/packages/effect/test/Brand.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Brand.test.ts rename to packages/effect/test/Brand.test.ts diff --git a/repos/effect/packages/effect/test/ByteSize.test.ts b/packages/effect/test/ByteSize.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ByteSize.test.ts rename to packages/effect/test/ByteSize.test.ts diff --git a/repos/effect/packages/effect/test/Cache.test.ts b/packages/effect/test/Cache.test.ts similarity index 98% rename from repos/effect/packages/effect/test/Cache.test.ts rename to packages/effect/test/Cache.test.ts index 7776eedcb..e04c577df 100644 --- a/repos/effect/packages/effect/test/Cache.test.ts +++ b/packages/effect/test/Cache.test.ts @@ -489,6 +489,38 @@ describe("Cache", () => { assert.strictEqual(lookups, 1) })) + it.effect("interrupting a get as its lookup starts abandons the entry", () => + Effect.gen(function*() { + const started = yield* Deferred.make() + let calls = 0 + let interrupted = false + const cache = yield* Cache.makeWith( + (_key: string) => + Effect.suspend(() => { + if (++calls > 1) return Effect.succeed("ok") + return Deferred.succeed(started, undefined).pipe( + Effect.andThen(Effect.never), + Effect.onInterrupt(() => + Effect.sync(() => { + interrupted = true + }) + ) + ) + }), + { capacity: 4, timeToLive: (exit) => Exit.isSuccess(exit) ? Duration.infinity : Duration.zero } + ) + + const getter = yield* Cache.get(cache, "key").pipe(Effect.forkChild) + yield* Deferred.await(started) + // Do not yield here: interruption must race with the lookup starting. + yield* Fiber.interrupt(getter) + + assert.strictEqual(yield* Cache.size(cache), 0) + assert.isTrue(interrupted) + assert.strictEqual(yield* Cache.get(cache, "key"), "ok") + assert.strictEqual(calls, 2) + })) + it.effect("concurrent access - interrupting the last consumer interrupts the lookup", () => Effect.gen(function*() { let lookupCount = 0 diff --git a/repos/effect/packages/effect/test/Cause.test.ts b/packages/effect/test/Cause.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Cause.test.ts rename to packages/effect/test/Cause.test.ts diff --git a/repos/effect/packages/effect/test/CauseMapAnnotations.test.ts b/packages/effect/test/CauseMapAnnotations.test.ts similarity index 100% rename from repos/effect/packages/effect/test/CauseMapAnnotations.test.ts rename to packages/effect/test/CauseMapAnnotations.test.ts diff --git a/repos/effect/packages/effect/test/Channel.test.ts b/packages/effect/test/Channel.test.ts similarity index 88% rename from repos/effect/packages/effect/test/Channel.test.ts rename to packages/effect/test/Channel.test.ts index 3ff55a3f8..a005b99ab 100644 --- a/repos/effect/packages/effect/test/Channel.test.ts +++ b/packages/effect/test/Channel.test.ts @@ -33,6 +33,20 @@ describe("Channel", () => { }) describe("constructors", () => { + for ( + const [name, constructor] of [["callback", Channel.callback], ["callbackArray", Channel.callbackArray]] as const + ) { + it.live(`${name} propagates registration effect failures`, () => + Effect.gen(function*() { + const exit = yield* constructor(() => Effect.fail("setup failed")).pipe( + Channel.runDrain, + Effect.timeout("500 millis"), + Effect.exit + ) + assert.deepStrictEqual(exit, Exit.fail("setup failed")) + })) + } + it.effect("empty", () => Effect.gen(function*() { const result = yield* Channel.empty.pipe( @@ -215,6 +229,32 @@ describe("Channel", () => { assert.isTrue(yield* Ref.get(released)) })) + it.effect("acquireUseRelease releases resource when interrupted during acquisition", () => + Effect.gen(function*() { + const started = yield* Deferred.make() + const gate = yield* Deferred.make() + const released: Array = [] + const acquire = Effect.gen(function*() { + yield* Deferred.succeed(started, undefined) + yield* Deferred.await(gate) + return 1 + }) + const fiber = yield* Effect.forkChild(Channel.runDrain(Channel.acquireUseRelease( + acquire, + () => Channel.never, + (resource) => + Effect.sync(() => { + released.push(resource) + }) + ))) + yield* Deferred.await(started) + yield* Effect.forkChild(Fiber.interrupt(fiber), { startImmediately: true }) + yield* Deferred.succeed(gate, undefined) + const exit = yield* Fiber.await(fiber) + assert.isTrue(Exit.hasInterrupts(exit)) + assert.deepStrictEqual(released, [1]) + })) + it.effect("acquireUseRelease combines usage and release failures", () => Effect.gen(function*() { const result = yield* Channel.acquireUseRelease( @@ -373,6 +413,32 @@ describe("Channel", () => { }) describe("merging", () => { + for (const kind of ["mergeAll", "concurrent flatMap"] as const) { + const flatten = (inner: Channel.Channel) => + kind === "mergeAll" + ? Channel.succeed(inner).pipe(Channel.mergeAll({ concurrency: 1 })) + : Channel.succeed(1).pipe(Channel.flatMap(() => inner, { concurrency: 2 })) + + it.effect(`${kind} surfaces inner release failure after successful usage`, () => + Effect.gen(function*() { + const inner = Channel.acquireRelease(Effect.succeed(1), () => Effect.die("release failure")) + const result = yield* flatten(inner).pipe(Channel.runCollect, Effect.exit) + assert.deepStrictEqual(result, Exit.die("release failure")) + })) + + it.effect(`${kind} combines inner usage and release failures`, () => + Effect.gen(function*() { + const inner = Channel.acquireRelease(Effect.succeed(1), () => Effect.die("release failure")).pipe( + Channel.mapEffect(() => Effect.fail("usage failure")) + ) + const result = yield* flatten(inner).pipe(Channel.runCollect, Effect.exit) + assert.deepStrictEqual( + result, + Exit.failCause(Cause.combine(Cause.fail("usage failure"), Cause.die("release failure"))) + ) + })) + } + it.effect("merge - interrupts left side if halt strategy is set to 'right'", () => Effect.gen(function*() { const latch = yield* Latch.make(false) @@ -433,6 +499,17 @@ describe("Channel", () => { assert.deepStrictEqual(result, [1, 2]) })) + it.live("merge - propagates a finalizer defect after the other side completes", () => + Effect.gen(function*() { + const left = Channel.acquireRelease(Effect.succeed(1), () => Effect.die("release defect")) + const result = yield* Channel.merge(left, Channel.empty).pipe( + Channel.runCollect, + Effect.timeout("500 millis"), + Effect.exit + ) + assert.deepStrictEqual(result, Exit.die("release defect")) + })) + it.effect("merge - prioritizes failure", () => Effect.gen(function*() { const left = Channel.fromEffect(Effect.fail("boom")) diff --git a/repos/effect/packages/effect/test/Chunk.test.ts b/packages/effect/test/Chunk.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Chunk.test.ts rename to packages/effect/test/Chunk.test.ts diff --git a/repos/effect/packages/effect/test/Clock.test.ts b/packages/effect/test/Clock.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Clock.test.ts rename to packages/effect/test/Clock.test.ts diff --git a/repos/effect/packages/effect/test/Combiner.test.ts b/packages/effect/test/Combiner.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Combiner.test.ts rename to packages/effect/test/Combiner.test.ts diff --git a/repos/effect/packages/effect/test/Config.test.ts b/packages/effect/test/Config.test.ts similarity index 98% rename from repos/effect/packages/effect/test/Config.test.ts rename to packages/effect/test/Config.test.ts index 3e9b37df7..1bc4f1db0 100644 --- a/repos/effect/packages/effect/test/Config.test.ts +++ b/packages/effect/test/Config.test.ts @@ -1,10 +1,12 @@ import { assert, describe, it } from "@effect/vitest" import { ByteSize, + Cause, Config, ConfigProvider, Duration, Effect, + Exit, Option, pipe, Redacted, @@ -88,6 +90,29 @@ describe("Config", () => { } })) + it.effect("does not recover source errors with finalizer defects", () => + Effect.gen(function*() { + const sourceError = new ConfigProvider.SourceError({ message: "read failed" }) + const cleanup = new Error("cleanup failed") + const provider = ConfigProvider.make(() => Effect.fail(sourceError).pipe(Effect.ensuring(Effect.die(cleanup)))) + const exit = yield* Effect.exit( + Config.String("value").pipe(Config.orElse(() => Config.succeed("fallback"))).parse(provider) + ) + + assert.deepStrictEqual( + exit, + Exit.failCause(Cause.combine(Cause.fail(new Config.ConfigError(sourceError)), Cause.die(cleanup))) + ) + })) + + it("preserves provider defects and interruption reasons", async () => { + const cause = Cause.combine(Cause.die(new Error("provider defect")), Cause.interrupt(123)) + const provider = ConfigProvider.make(() => Effect.failCause(cause)) + const exit = await Effect.runPromiseExit(Config.String("value").parse(provider)) + + assert.deepStrictEqual(exit, Exit.failCause(cause)) + }) + describe("constructors", () => { it("fail creates an always-failing config", async () => { await assertFailure( @@ -1685,13 +1710,38 @@ Expected "Infinity" | "-Infinity" | "NaN" ) }) + it("keeps key-value separators inside values", async () => { + const config = Config.Record(Schema.String, Schema.String, "OTEL_EXPORTER_OTLP_HEADERS") + + await assertSuccess( + config, + ConfigProvider.fromEnv({ + env: { + OTEL_EXPORTER_OTLP_HEADERS: "authorization=Basic dXNlcjpwYXNz==,x-tenant=a=b" + } + }), + { + "authorization": "Basic dXNlcjpwYXNz==", + "x-tenant": "a=b" + } + ) + }) + + it("supports an empty key-value separator", async () => { + await assertSuccess( + Config.Record(Schema.String, Schema.String, { keyValueSeparator: "" }), + ConfigProvider.fromUnknown("ab,cd"), + { a: "b", c: "d" } + ) + }) + it("supports custom separators", async () => { const options = { separator: "&", keyValueSeparator: "==" } - const input = "service.name==my-service&service.version==1.0.0&custom.attribute==value" + const input = "service.name==my-service&service.version==1.0.0&custom.attribute==a==b" const expected = { "service.name": "my-service", "service.version": "1.0.0", - "custom.attribute": "value" + "custom.attribute": "a==b" } await assertSuccess( diff --git a/repos/effect/packages/effect/test/ConfigProvider.test.ts b/packages/effect/test/ConfigProvider.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ConfigProvider.test.ts rename to packages/effect/test/ConfigProvider.test.ts diff --git a/repos/effect/packages/effect/test/Context.test.ts b/packages/effect/test/Context.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Context.test.ts rename to packages/effect/test/Context.test.ts diff --git a/repos/effect/packages/effect/test/Cron.test.ts b/packages/effect/test/Cron.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Cron.test.ts rename to packages/effect/test/Cron.test.ts index d4d942662..4f693f6a3 100644 --- a/repos/effect/packages/effect/test/Cron.test.ts +++ b/packages/effect/test/Cron.test.ts @@ -760,6 +760,24 @@ describe("Cron", () => { deepStrictEqual(next().pipe(DateTime.formatIsoZoned), e.pipe(DateTime.formatIsoZoned)) }) + it("next skips the repeated hour when starting in the second DST fold hour", () => { + const cron = Cron.parseUnsafe("30 * * * *", "Europe/Berlin") + // Ambiguous times run only once, so both occurrences of 02:30 must be skipped. + deepStrictEqual( + next(cron, new Date("2024-10-27T02:15:00+01:00")), + new Date("2024-10-27T03:30:00+01:00") + ) + }) + + it("next skips the repeated interval when starting in a three-hour fold", () => { + const cron = Cron.parseUnsafe("* * * * * *", "Antarctica/Casey") + // Casey rolls back from +11:00 to +08:00, repeating 00:00 through 02:59:59. + deepStrictEqual( + next(cron, new Date("2023-03-09T00:00:00+08:00")), + new Date("2023-03-09T03:00:00+08:00") + ) + }) + it("handles utc timezone", () => { const utc = DateTime.zoneMakeNamedUnsafe("UTC") const make = (date: string): DateTime.Zoned => Option.getOrThrow(DateTime.makeZonedFromString(date)) diff --git a/repos/effect/packages/effect/test/Crypto.test.ts b/packages/effect/test/Crypto.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Crypto.test.ts rename to packages/effect/test/Crypto.test.ts diff --git a/repos/effect/packages/effect/test/Data.test.ts b/packages/effect/test/Data.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Data.test.ts rename to packages/effect/test/Data.test.ts diff --git a/repos/effect/packages/effect/test/DateTime.test.ts b/packages/effect/test/DateTime.test.ts similarity index 100% rename from repos/effect/packages/effect/test/DateTime.test.ts rename to packages/effect/test/DateTime.test.ts diff --git a/repos/effect/packages/effect/test/Deferred.test.ts b/packages/effect/test/Deferred.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Deferred.test.ts rename to packages/effect/test/Deferred.test.ts diff --git a/repos/effect/packages/effect/test/Duration.test.ts b/packages/effect/test/Duration.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Duration.test.ts rename to packages/effect/test/Duration.test.ts diff --git a/repos/effect/packages/effect/test/Effect.test.ts b/packages/effect/test/Effect.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Effect.test.ts rename to packages/effect/test/Effect.test.ts index 609e1f06b..e10953d51 100644 --- a/repos/effect/packages/effect/test/Effect.test.ts +++ b/packages/effect/test/Effect.test.ts @@ -663,6 +663,33 @@ describe("Effect", () => { })) }) + describe("effectify", () => { + it.effect("turns a throwing onError mapper in an async callback into a defect", () => + Effect.gen(function*() { + let callback!: (error: Error | null, value?: string) => void + let finalized = false + const defect = new Error("mapper") + const effectified = Effect.effectify( + (cb: (error: Error | null, value?: string) => void) => { + callback = cb + }, + () => { + throw defect + } + ) + const fiber = yield* effectified().pipe( + Effect.ensuring(Effect.sync(() => { + finalized = true + })), + Effect.forkChild({ startImmediately: true }) + ) + // Invoke after registration returns, outside effectify's synchronous try/catch. + assert.doesNotThrow(() => callback(new Error("source"))) + assertExitDefect(yield* Fiber.await(fiber), defect) + assert.isTrue(finalized) + })) + }) + describe("gen", () => { it("gen", () => Effect.gen(function*() { @@ -1443,6 +1470,15 @@ describe("Effect", () => { } describe("repeat", () => { + it.effect("preserves finalizer defects accompanying schedule completion", () => + Effect.gen(function*() { + const policy = Schedule.fromStep( + Effect.succeed(() => Cause.done(42).pipe(Effect.ensuring(Effect.die("cleanup failed")))) + ) + const exit = yield* Effect.exit(Effect.repeat(Effect.void, policy)) + assert.deepStrictEqual(exit, Exit.die("cleanup failed")) + })) + it.effect("is interruptible", () => Effect.gen(function*() { const fiber = yield* Effect.void.pipe( @@ -1572,6 +1608,18 @@ describe("Effect", () => { }) describe("retry", () => { + it.effect("does not retry typed failures accompanied by finalizer defects", () => + Effect.gen(function*() { + let attempts = 0 + const source = Effect.suspend(() => { + attempts++ + return Effect.fail("error").pipe(Effect.ensuring(Effect.die("cleanup failed"))) + }) + const exit = yield* Effect.exit(Effect.retry(source, Schedule.recurs(1))) + assert.strictEqual(attempts, 1) + assert.deepStrictEqual(exit, Exit.failCause(Cause.combine(Cause.fail("error"), Cause.die("cleanup failed")))) + })) + it.live("nothing on success", () => Effect.gen(function*() { let count = 0 @@ -3825,6 +3873,19 @@ describe("Effect", () => { }) describe("catchCause", () => { + it("repeated throwing handlers do not overflow the stack and run finalizers", () => { + let program: Effect.Effect = Effect.die("initial") + for (let i = 0; i < 20_000; i++) { + program = Effect.catchCause(program, () => { + throw "handler defect" + }) + } + let finalized = 0 + const exit = Effect.runSyncExit(Effect.ensuring(program, Effect.sync(() => finalized++))) + assert.deepStrictEqual(exit, Exit.die("handler defect")) + assert.strictEqual(finalized, 1) + }) + it.effect("first argument as success", () => Effect.gen(function*() { const result = yield* Effect.catchCause(Effect.succeed(1), () => Effect.fail("e2" as const)) @@ -3857,6 +3918,20 @@ describe("Effect", () => { assert.strictEqual(val2, 200) })) + it.effect("should publish child transaction writes after the enclosing transaction completes", () => + Effect.gen(function*() { + const ref = TxRef.makeUnsafe(0) + const gate = yield* Deferred.make() + const child = yield* Effect.tx( + Effect.forkChild(Deferred.await(gate).pipe(Effect.andThen(Effect.tx(TxRef.set(ref, 42))))) + ) + + yield* Deferred.succeed(gate, undefined) + yield* Fiber.join(child) + + assert.strictEqual(yield* TxRef.get(ref), 42) + })) + it.effect("should allow TxRef.modify outside an existing transaction", () => Effect.gen(function*() { const ref = TxRef.makeUnsafe(0) @@ -4002,6 +4077,29 @@ describe("Effect", () => { }) describe("retry", () => { + it.effect("should preserve a retry cleanup failure when a read ref changes during cleanup", () => + Effect.gen(function*() { + const ref = TxRef.makeUnsafe(0) + const cleaning = yield* Deferred.make() + const release = yield* Deferred.make() + const transaction = yield* Effect.tx(Effect.gen(function*() { + if ((yield* TxRef.get(ref)) !== 0) return + return yield* Effect.txRetry.pipe(Effect.onExit(() => + Deferred.succeed(cleaning, undefined).pipe( + Effect.andThen(Deferred.await(release)), + Effect.andThen(Effect.fail("release-error")) + ) + )) + })).pipe(Effect.forkChild) + + yield* Deferred.await(cleaning) + yield* Effect.tx(TxRef.set(ref, 1)) + yield* Deferred.succeed(release, undefined) + + const error = yield* Fiber.join(transaction).pipe(Effect.flip) + assert.strictEqual(error, "release-error") + })) + it.effect("should rerun when a read ref changed while the transaction was suspended", () => Effect.gen(function*() { const ref = TxRef.makeUnsafe(0) diff --git a/repos/effect/packages/effect/test/EffectEager.test.ts b/packages/effect/test/EffectEager.test.ts similarity index 100% rename from repos/effect/packages/effect/test/EffectEager.test.ts rename to packages/effect/test/EffectEager.test.ts diff --git a/repos/effect/packages/effect/test/EffectKeepAlive.test.ts b/packages/effect/test/EffectKeepAlive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/EffectKeepAlive.test.ts rename to packages/effect/test/EffectKeepAlive.test.ts diff --git a/repos/effect/packages/effect/test/Effectable.test.ts b/packages/effect/test/Effectable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Effectable.test.ts rename to packages/effect/test/Effectable.test.ts diff --git a/repos/effect/packages/effect/test/Equal.test.ts b/packages/effect/test/Equal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Equal.test.ts rename to packages/effect/test/Equal.test.ts diff --git a/repos/effect/packages/effect/test/Equivalence.test.ts b/packages/effect/test/Equivalence.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Equivalence.test.ts rename to packages/effect/test/Equivalence.test.ts diff --git a/repos/effect/packages/effect/test/ExecutionPlan.test.ts b/packages/effect/test/ExecutionPlan.test.ts similarity index 96% rename from repos/effect/packages/effect/test/ExecutionPlan.test.ts rename to packages/effect/test/ExecutionPlan.test.ts index 905a5b653..e1fc96a7b 100644 --- a/repos/effect/packages/effect/test/ExecutionPlan.test.ts +++ b/packages/effect/test/ExecutionPlan.test.ts @@ -11,6 +11,7 @@ import { Fiber, Latch, Layer, + Schedule, Scheduler, Stream } from "effect" @@ -100,6 +101,24 @@ describe("ExecutionPlan", () => { ) strictEqual(result, "ok") })) + + it.effect("captures schedule requirements", () => + Effect.gen(function*() { + const schedulePlan = ExecutionPlan.make({ + provide: Layer.empty, + schedule: Schedule.recurs(1).pipe( + Schedule.map(() => + Effect.flatMap(Policy, ({ allow }) => allow ? Effect.void : Effect.fail("denied" as const)) + ) + ) + }) + const captured = yield* schedulePlan.captureRequirements.pipe(Effect.provideService(Policy, { allow: true })) + for (const ambient of [undefined, { allow: false }]) { + const program = Effect.withExecutionPlan(failOnce(), captured) + const result = yield* ambient ? Effect.provideService(program, Policy, ambient) : program + strictEqual(result, "ok") + } + })) }) describe("Stream.withExecutionPlan", () => { diff --git a/repos/effect/packages/effect/test/Exit.test.ts b/packages/effect/test/Exit.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Exit.test.ts rename to packages/effect/test/Exit.test.ts diff --git a/repos/effect/packages/effect/test/Fiber.test.ts b/packages/effect/test/Fiber.test.ts similarity index 92% rename from repos/effect/packages/effect/test/Fiber.test.ts rename to packages/effect/test/Fiber.test.ts index ea5ca64c1..b929f54c5 100644 --- a/repos/effect/packages/effect/test/Fiber.test.ts +++ b/packages/effect/test/Fiber.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Cause, Context, Effect, Exit, Fiber, Latch, References } from "effect" +import { Cause, Context, Effect, Exit, Fiber, Latch, References, Scope } from "effect" describe("Fiber", () => { it("is a fiber", async () => { @@ -7,6 +7,18 @@ describe("Fiber", () => { assert.isTrue(Fiber.isFiber(result)) }) + it("runIn closes the latest registration first when a fiber is registered twice", async () => { + const events: Array = [] + const scope = Scope.makeUnsafe("sequential") + const fiber = Effect.runFork(Effect.never.pipe(Effect.ensuring(Effect.sync(() => events.push("interrupted"))))) + Fiber.runIn(fiber, scope) + Effect.runSync(Scope.addFinalizer(scope, Effect.sync(() => events.push("finalizer")))) + Fiber.runIn(fiber, scope) + + await Effect.runPromise(Scope.close(scope, Exit.void)) + assert.deepStrictEqual(events, ["interrupted", "finalizer"]) + }) + it("notifies all observers when an observer cancels during exit", () => { const fiber = Effect.runFork(Effect.never) const observed: Array = [] diff --git a/repos/effect/packages/effect/test/FiberHandle.test.ts b/packages/effect/test/FiberHandle.test.ts similarity index 89% rename from repos/effect/packages/effect/test/FiberHandle.test.ts rename to packages/effect/test/FiberHandle.test.ts index b0abfc16d..a247c752b 100644 --- a/repos/effect/packages/effect/test/FiberHandle.test.ts +++ b/packages/effect/test/FiberHandle.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { assertFalse, assertTrue, strictEqual } from "@effect/vitest/utils" -import { Deferred, Effect, Exit, Fiber, FiberHandle, Option, pipe, Ref } from "effect" +import { Deferred, Effect, Exit, Fiber, FiberHandle, Option, pipe, Ref, Scope } from "effect" import { TestClock } from "effect/testing" const makeWorker = Effect.gen(function*() { @@ -49,6 +49,28 @@ describe("FiberHandle", () => { strictEqual(yield* (Ref.get(ref)), 1) })) + it.effect("retains ownership of a replacement made by a synchronous finalizer", () => + Effect.gen(function*() { + const scope = yield* Scope.make() + const handle = yield* FiberHandle.make().pipe(Scope.provide(scope)) + const run = yield* FiberHandle.runtime(handle)() + const fibers: Array> = [] + yield* Effect.addFinalizer(() => Fiber.interruptAll(fibers)) + + const previous = run(Effect.never.pipe(Effect.ensuring(Effect.sync(() => { + fibers.push(run(Effect.never)) + })))) + const replacement = run(Effect.never) + fibers.push(previous, replacement) + assert.strictEqual(fibers.length, 3) + + yield* Scope.close(scope, Exit.void) + + assert.isDefined(previous.pollUnsafe()) + assert.isDefined(replacement.pollUnsafe()) + assert.isDefined(fibers[0].pollUnsafe(), "fiber started by the finalizer still running after scope close") + })) + it.effect("runtime", () => Effect.gen(function*() { const ref = yield* Ref.make(0) diff --git a/repos/effect/packages/effect/test/FiberMap.test.ts b/packages/effect/test/FiberMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberMap.test.ts rename to packages/effect/test/FiberMap.test.ts diff --git a/repos/effect/packages/effect/test/FiberSet.test.ts b/packages/effect/test/FiberSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberSet.test.ts rename to packages/effect/test/FiberSet.test.ts diff --git a/repos/effect/packages/effect/test/FiberSetRuntimePropagation.test.ts b/packages/effect/test/FiberSetRuntimePropagation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberSetRuntimePropagation.test.ts rename to packages/effect/test/FiberSetRuntimePropagation.test.ts diff --git a/repos/effect/packages/effect/test/FileSystem.test-utils.ts b/packages/effect/test/FileSystem.test-utils.ts similarity index 100% rename from repos/effect/packages/effect/test/FileSystem.test-utils.ts rename to packages/effect/test/FileSystem.test-utils.ts diff --git a/repos/effect/packages/effect/test/Formatter.test.ts b/packages/effect/test/Formatter.test.ts similarity index 92% rename from repos/effect/packages/effect/test/Formatter.test.ts rename to packages/effect/test/Formatter.test.ts index 9040fa030..f25972bad 100644 --- a/repos/effect/packages/effect/test/Formatter.test.ts +++ b/packages/effect/test/Formatter.test.ts @@ -13,6 +13,7 @@ import { SchemaParser } from "effect" import { format, formatJson } from "effect/Formatter" +import { runInNewContext } from "node:vm" import { assertFalse, assertTrue, deepStrictEqual, strictEqual } from "./utils/assert.ts" class SensitiveData implements Redactable.Redactable { @@ -338,6 +339,22 @@ describe("Formatter", () => { strictEqual(formatJson({ left: shared, right: shared }), `{"left":{"a":1},"right":{"a":1}}`) }) + it.each([ + ["Number", Object(42), `42`], + ["Boolean", Object(false), `false`], + ["String", Object("abc"), `"abc"`], + ["cross-realm Number", runInNewContext("new Number(42)"), `42`], + ["cross-realm Boolean", runInNewContext("new Boolean(false)"), `false`], + ["cross-realm String", runInNewContext("new String(\"abc\")"), `"abc"`] + ])("should serialize boxed %s values", (_, value, expected) => { + strictEqual(formatJson(value), expected) + strictEqual(formatJson({ value }), `{"value":${expected}}`) + // JSON.stringify unboxes wrappers and ignores their own properties + Object.defineProperty(value, "extra", { get: () => 1, enumerable: true }) + strictEqual(formatJson(value), expected) + strictEqual(formatJson({ value }), `{"value":${expected}}`) + }) + it("should stringify BigInt values", () => { strictEqual(formatJson(123n), `"123n"`) strictEqual(formatJson({ value: 123n }), `{"value":"123n"}`) @@ -383,6 +400,51 @@ describe("Formatter", () => { strictEqual(formatJson([data]), `[{"secret":"[REDACTED]"}]`) strictEqual(formatJson(date), `"[REDACTED]"`) }) + + it("should redact sensitive data returned from a getter", () => { + strictEqual( + formatJson({ + get a() { + return data + } + }), + `{"a":{"secret":"[REDACTED]"}}` + ) + }) + + it("should read getters once with the original receiver", () => { + let receiverIsHolder = false + let reads = 0 + const holder = { + get a() { + receiverIsHolder = Object.is(this, holder) + // unstable getter: a second read would leak the raw secret + return reads++ === 0 ? data : { secret: "my-secret-key" } + } + } + + strictEqual(formatJson(holder), `{"a":{"secret":"[REDACTED]"}}`) + strictEqual(reads, 1) + assertTrue(receiverIsHolder) + }) + + it("should redact sensitive data returned from a frozen accessor", () => { + strictEqual( + formatJson(Object.freeze({ + get a() { + return data + } + })), + `{"a":{"secret":"[REDACTED]"}}` + ) + }) + + it("should redact sensitive data returned from a non-enumerable array index getter", () => { + const array: Array = [] + // JSON.stringify reads array indices regardless of enumerability + Object.defineProperty(array, "0", { get: () => data }) + strictEqual(formatJson(array), `[{"secret":"[REDACTED]"}]`) + }) }) describe("Inspectable.toJson", () => { diff --git a/repos/effect/packages/effect/test/Function.test.ts b/packages/effect/test/Function.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Function.test.ts rename to packages/effect/test/Function.test.ts diff --git a/repos/effect/packages/effect/test/Graph.test.ts b/packages/effect/test/Graph.test.ts similarity index 96% rename from repos/effect/packages/effect/test/Graph.test.ts rename to packages/effect/test/Graph.test.ts index f064afbe6..8347b1146 100644 --- a/repos/effect/packages/effect/test/Graph.test.ts +++ b/packages/effect/test/Graph.test.ts @@ -1,5 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Equal, Graph, Hash, Option } from "effect" +import { runInNewContext } from "node:vm" const directed = ( nodes: ReadonlyArray, @@ -1997,6 +1998,81 @@ describe("Graph", () => { } }) + for (const [name, cycleWeight, distance] of [["integer", 16, 1], ["floating-point", 0.1, 0.01]] as const) { + it(`reconstructs Floyd-Warshall paths with ${name} weights on a zero-cost cycle`, () => { + const graph = directed([0, 1, 2], [[0, 1, cycleWeight], [1, 0, -cycleWeight], [0, 2, distance]]) + // A Vitest timeout cannot interrupt synchronous path reconstruction if next hops form a cycle. + const result: Graph.AllPairsResult = runInNewContext( + "Graph.floydWarshall(graph, (edge) => edge)", + { Graph, graph }, + { timeout: 1000 } + ) + assert.deepStrictEqual(result.paths.get(0)?.get(2), [0, 2]) + assert.deepStrictEqual(result.edges.get(0)?.get(2), [2]) + assert.deepStrictEqual(result.costs.get(0)?.get(2), [distance]) + assert.closeTo(result.distances.get(0)!.get(2)!, distance, 1e-15) + }) + } + + it("reconstructs every Floyd-Warshall path through overlapping zero-cost cycles", () => { + const connections: Array<[number, number, number]> = [ + [0, 1, 0.1], + [1, 0, -0.1], + [1, 2, -0.1], + [2, 1, 0.1], + [2, 3, 0.1], + [3, 2, -0.1], + [0, 4, 0.01], + [4, 5, 0], + [5, 0, 1] + ] + const graph = directed([0, 1, 2, 3, 4, 5], connections) + const result: Graph.AllPairsResult = runInNewContext( + "Graph.floydWarshall(graph, (edge) => edge)", + { Graph, graph }, + { timeout: 1000 } + ) + const distances = [ + [0, 0.1, 0, 0.1, 0.01, 0.01], + [-0.1, 0, -0.1, 0, -0.09, -0.09], + [0, 0.1, 0, 0.1, 0.01, 0.01], + [-0.1, 0, -0.1, 0, -0.09, -0.09], + [1, 1.1, 1, 1.1, 0, 0], + [1, 1.1, 1, 1.1, 1.01, 0] + ] + for (let source = 0; source < 6; source++) { + for (let target = 0; target < 6; target++) { + const path = result.paths.get(source)!.get(target)! + const edges = result.edges.get(source)!.get(target)! + const costs = result.costs.get(source)!.get(target)! + assert.strictEqual(path[0], source) + assert.strictEqual(path[path.length - 1], target) + assert.strictEqual(new Set(path).size, path.length) + assert.strictEqual(edges.length, path.length - 1) + assert.strictEqual(costs.length, edges.length) + for (let i = 0; i < edges.length; i++) { + assert.deepStrictEqual(connections[edges[i]], [path[i], path[i + 1], costs[i]]) + } + assert.closeTo(result.distances.get(source)!.get(target)!, distances[source][target], 1e-15) + assert.closeTo(costs.reduce((sum, cost) => sum + cost, 0), distances[source][target], 1e-15) + } + } + }) + + it("preserves tiny Floyd-Warshall improvements", () => { + const graph = directed([0, 1, 2], [[0, 2, 3e-20], [0, 1, 1e-20], [1, 2, 1e-20]]) + const result = Graph.floydWarshall(graph, (edge) => edge) + assert.strictEqual(result.distances.get(0)?.get(2), 2e-20) + assert.deepStrictEqual(result.paths.get(0)?.get(2), [0, 1, 2]) + assert.deepStrictEqual(result.edges.get(0)?.get(2), [1, 2]) + assert.deepStrictEqual(result.costs.get(0)?.get(2), [1e-20, 1e-20]) + }) + + it("detects tiny Floyd-Warshall negative cycles", () => { + const graph = directed([0, 1], [[0, 1, 1e-20], [1, 0, -2e-20]]) + assertGraphError(() => Graph.floydWarshall(graph, (edge) => edge), "Negative cycle detected involving node 0") + }) + it("preserves null edge payloads in Floyd-Warshall multihop paths", () => { type Edge = null | { readonly weight: number } const graph = directed(["A", "B", "C"], [ diff --git a/repos/effect/packages/effect/test/Hash.test.ts b/packages/effect/test/Hash.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Hash.test.ts rename to packages/effect/test/Hash.test.ts diff --git a/repos/effect/packages/effect/test/HashMap.test.ts b/packages/effect/test/HashMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashMap.test.ts rename to packages/effect/test/HashMap.test.ts diff --git a/repos/effect/packages/effect/test/HashRing.test.ts b/packages/effect/test/HashRing.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashRing.test.ts rename to packages/effect/test/HashRing.test.ts diff --git a/repos/effect/packages/effect/test/HashSet.test.ts b/packages/effect/test/HashSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashSet.test.ts rename to packages/effect/test/HashSet.test.ts diff --git a/repos/effect/packages/effect/test/HttpClient.test.ts b/packages/effect/test/HttpClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HttpClient.test.ts rename to packages/effect/test/HttpClient.test.ts diff --git a/repos/effect/packages/effect/test/Iterable.test.ts b/packages/effect/test/Iterable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Iterable.test.ts rename to packages/effect/test/Iterable.test.ts diff --git a/repos/effect/packages/effect/test/JsonPatch.test.ts b/packages/effect/test/JsonPatch.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonPatch.test.ts rename to packages/effect/test/JsonPatch.test.ts diff --git a/repos/effect/packages/effect/test/JsonPointer.test.ts b/packages/effect/test/JsonPointer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonPointer.test.ts rename to packages/effect/test/JsonPointer.test.ts diff --git a/repos/effect/packages/effect/test/JsonSchema.test.ts b/packages/effect/test/JsonSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonSchema.test.ts rename to packages/effect/test/JsonSchema.test.ts diff --git a/repos/effect/packages/effect/test/Latch.test.ts b/packages/effect/test/Latch.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Latch.test.ts rename to packages/effect/test/Latch.test.ts diff --git a/repos/effect/packages/effect/test/Layer.test.ts b/packages/effect/test/Layer.test.ts similarity index 96% rename from repos/effect/packages/effect/test/Layer.test.ts rename to packages/effect/test/Layer.test.ts index 879f4cd52..9b987064c 100644 --- a/repos/effect/packages/effect/test/Layer.test.ts +++ b/packages/effect/test/Layer.test.ts @@ -486,6 +486,29 @@ describe("Layer", () => { }) describe("MemoMap", () => { + it.effect("retrying a buildWithMemoMap effect keeps the resource alive until its scope closes", () => + Effect.gen(function*() { + const Resource = Context.Service<{ live: boolean }>("Resource") + let attempts = 0 + const layer = Layer.effect( + Resource, + Effect.suspend(() => + ++attempts === 1 + ? Effect.fail("transient") + : Effect.acquireRelease(Effect.succeed<{ live: boolean }>({ live: true }), (resource) => + Effect.sync(() => resource.live = false)) + ) + ) + const owner = yield* Effect.acquireRelease(Scope.make(), (scope, exit) => Scope.close(scope, exit)) + const build = Layer.buildWithMemoMap(layer, Layer.makeMemoMapUnsafe(), owner) + const context = yield* Effect.retry(build, { times: 1 }) + const resource = Context.get(context, Resource) + + assert.isTrue(resource.live) + yield* Scope.close(owner, Exit.void) + assert.isFalse(resource.live) + }).pipe(Effect.scoped)) + class Shared extends Context.Service()("Shared") {} const releaseCounted = (released: Ref.Ref) => diff --git a/repos/effect/packages/effect/test/LayerMap.test.ts b/packages/effect/test/LayerMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LayerMap.test.ts rename to packages/effect/test/LayerMap.test.ts diff --git a/repos/effect/packages/effect/test/LayerRef.test.ts b/packages/effect/test/LayerRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LayerRef.test.ts rename to packages/effect/test/LayerRef.test.ts diff --git a/repos/effect/packages/effect/test/LogLevel.test.ts b/packages/effect/test/LogLevel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LogLevel.test.ts rename to packages/effect/test/LogLevel.test.ts diff --git a/repos/effect/packages/effect/test/Logger.test.ts b/packages/effect/test/Logger.test.ts similarity index 78% rename from repos/effect/packages/effect/test/Logger.test.ts rename to packages/effect/test/Logger.test.ts index 78b8d45e1..e36628296 100644 --- a/repos/effect/packages/effect/test/Logger.test.ts +++ b/packages/effect/test/Logger.test.ts @@ -1,10 +1,11 @@ import { assert, describe, it } from "@effect/vitest" -import { Exit, Scope } from "effect" +import { Deferred, Exit, Fiber, Scope } from "effect" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" import * as References from "effect/References" +import * as TestClock from "effect/testing/TestClock" import * as TestConsole from "effect/testing/TestConsole" describe("Logger", () => { @@ -217,4 +218,55 @@ describe("Logger", () => { assert.strictEqual(result[2], "second") assert.match(result[3] as string, /boom/) })) + + it.effect("batched final flush uses the construction context", () => + Effect.gen(function*() { + const output: Array = [] + const constructionLogger = Logger.make((options) => { + output.push(`construction:${String(options.message)}`) + }) + const closingLogger = Logger.make((options) => { + output.push(`closing:${String(options.message)}`) + }) + const scope = yield* Scope.make() + const logger = yield* Logger.batched(Logger.make((options) => String(options.message)), { + window: "1 hour", + flush: (batch) => Effect.log(batch.join(",")) + }).pipe(Scope.provide(scope), Effect.provide(Logger.layer([constructionLogger]))) + + yield* Effect.log("buffered").pipe(Effect.provide(Logger.layer([logger]))) + yield* Scope.close(scope, Exit.void).pipe(Effect.provide(Logger.layer([closingLogger]))) + + assert.deepStrictEqual(output, ["construction:buffered"]) + })) + + it.effect("batched finishes an in-flight flush before flushing remaining entries on scope close", () => + Effect.gen(function*() { + const events: Array = [] + const started = yield* Deferred.make() + const scope = yield* Scope.make() + const logger = yield* Logger.batched(Logger.make((options) => String(options.message)), { + window: 1, + flush: (batch) => + Effect.gen(function*() { + events.push(`start:${batch.join(",")}`) + if (batch[0] === "first") { + yield* Deferred.succeed(started, undefined) + yield* Effect.sleep(50) + } + events.push(`end:${batch.join(",")}`) + }) + }).pipe(Scope.provide(scope)) + + yield* Effect.log("first").pipe(Effect.provide(Logger.layer([logger]))) + yield* TestClock.adjust(1) + yield* Deferred.await(started) + yield* Effect.log("second").pipe(Effect.provide(Logger.layer([logger]))) + + const closing = yield* Effect.forkChild(Scope.close(scope, Exit.void), { startImmediately: true }) + yield* TestClock.adjust(50) + yield* Fiber.join(closing) + + assert.deepStrictEqual(events, ["start:first", "end:first", "start:second", "end:second"]) + })) }) diff --git a/repos/effect/packages/effect/test/ManagedRuntime.test.ts b/packages/effect/test/ManagedRuntime.test.ts similarity index 65% rename from repos/effect/packages/effect/test/ManagedRuntime.test.ts rename to packages/effect/test/ManagedRuntime.test.ts index 4b121cf6d..e52ad5059 100644 --- a/repos/effect/packages/effect/test/ManagedRuntime.test.ts +++ b/packages/effect/test/ManagedRuntime.test.ts @@ -71,6 +71,81 @@ describe("ManagedRuntime", () => { assert.isTrue(Exit.hasInterrupts(exit)) }) + test("disposeEffect inside runPromise after an async boundary releases the layer", async () => { + let released = false + const runtime = ManagedRuntime.make(Layer.effectDiscard(Effect.addFinalizer(() => + Effect.sync(() => { + released = true + }) + ))) + + // Bound the deadlock without waiting for the uninterruptible disposal fiber. + const result = await Promise.race([ + runtime.runPromise(Effect.sleep(1).pipe(Effect.ensuring(runtime.disposeEffect))).then(() => "settled"), + new Promise((resolve) => setTimeout(() => resolve("timed out"), 500)) + ]) + assert.deepEqual({ result, released }, { result: "settled", released: true }) + }) + + test("disposeEffect inside an immediately started request child releases the layer", async () => { + let released = false + const runtime = ManagedRuntime.make(Layer.effectDiscard(Effect.addFinalizer(() => + Effect.sync(() => { + released = true + }) + ))) + + // Bound the deadlock without waiting for the uninterruptible disposal fiber. + const result = await Promise.race([ + runtime.runPromise(Effect.gen(function*() { + yield* Effect.sleep(1) + const child = yield* Effect.forkChild(runtime.disposeEffect, { startImmediately: true }) + yield* Fiber.join(child) + })).then(() => "settled"), + new Promise((resolve) => setTimeout(() => resolve("timed out"), 500)) + ]) + assert.deepEqual({ result, released }, { result: "settled", released: true }) + }) + + test("detached-fiber disposal still interrupts and awaits its former request", async () => { + const events: Array = [] + const runtime = ManagedRuntime.make(Layer.effectDiscard(Effect.addFinalizer(() => + Effect.sync(() => { + events.push("layer released") + }) + ))) + + const disposing = Effect.runPromise(Effect.gen(function*() { + const gate = yield* Deferred.make() + const started = yield* Deferred.make>() + runtime.runFork( + Effect.gen(function*() { + const detached = yield* Effect.forkDetach( + Deferred.await(gate).pipe(Effect.ensuring(runtime.disposeEffect)) + ) + yield* Deferred.succeed(started, detached) + return yield* Effect.never + }).pipe(Effect.ensuring(Effect.gen(function*() { + yield* Effect.sleep(1) + events.push("former request cleaned up") + }))) + ) + const detached = yield* Deferred.await(started) + yield* Deferred.succeed(gate, undefined) + yield* Fiber.join(detached) + })) + + // Bound the deadlock without waiting for the uninterruptible disposal fiber. + const result = await Promise.race([ + disposing.then(() => "settled"), + new Promise((resolve) => setTimeout(() => resolve("timed out"), 500)) + ]) + assert.deepEqual({ result, events }, { + result: "settled", + events: ["former request cleaned up", "layer released"] + }) + }) + for (const method of ["disposeEffect", "dispose"] as const) { it(`finishes request cleanup before releasing layer resources with ${method}`, async () => { const events = await Effect.runPromise(Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/Match.test.ts b/packages/effect/test/Match.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Match.test.ts rename to packages/effect/test/Match.test.ts diff --git a/repos/effect/packages/effect/test/Metric.test.ts b/packages/effect/test/Metric.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Metric.test.ts rename to packages/effect/test/Metric.test.ts diff --git a/repos/effect/packages/effect/test/Migrator.test.ts b/packages/effect/test/Migrator.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Migrator.test.ts rename to packages/effect/test/Migrator.test.ts diff --git a/repos/effect/packages/effect/test/MutableHashMap.test.ts b/packages/effect/test/MutableHashMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableHashMap.test.ts rename to packages/effect/test/MutableHashMap.test.ts diff --git a/repos/effect/packages/effect/test/MutableHashSet.test.ts b/packages/effect/test/MutableHashSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableHashSet.test.ts rename to packages/effect/test/MutableHashSet.test.ts diff --git a/repos/effect/packages/effect/test/MutableList.test.ts b/packages/effect/test/MutableList.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableList.test.ts rename to packages/effect/test/MutableList.test.ts diff --git a/repos/effect/packages/effect/test/Newtype.test.ts b/packages/effect/test/Newtype.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Newtype.test.ts rename to packages/effect/test/Newtype.test.ts diff --git a/repos/effect/packages/effect/test/Number.test.ts b/packages/effect/test/Number.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Number.test.ts rename to packages/effect/test/Number.test.ts diff --git a/repos/effect/packages/effect/test/Optic.test.ts b/packages/effect/test/Optic.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Optic.test.ts rename to packages/effect/test/Optic.test.ts diff --git a/repos/effect/packages/effect/test/Option.test.ts b/packages/effect/test/Option.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Option.test.ts rename to packages/effect/test/Option.test.ts diff --git a/repos/effect/packages/effect/test/Order.test.ts b/packages/effect/test/Order.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Order.test.ts rename to packages/effect/test/Order.test.ts diff --git a/repos/effect/packages/effect/test/Ordering.test.ts b/packages/effect/test/Ordering.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Ordering.test.ts rename to packages/effect/test/Ordering.test.ts diff --git a/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts b/packages/effect/test/PartitionedSemaphore.test.ts similarity index 82% rename from repos/effect/packages/effect/test/PartitionedSemaphore.test.ts rename to packages/effect/test/PartitionedSemaphore.test.ts index 1fcd5f287..83cb20275 100644 --- a/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts +++ b/packages/effect/test/PartitionedSemaphore.test.ts @@ -2,6 +2,51 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, Exit, Fiber, Option, PartitionedSemaphore, Scheduler } from "effect" describe("PartitionedSemaphore", () => { + it.effect("a resumed waiter can immediately reacquire released permits", () => + Effect.gen(function*() { + const sem = yield* PartitionedSemaphore.make({ permits: 1 }) + yield* sem.take("holder", 1) + const waiter = yield* Effect.gen(function*() { + yield* sem.take("waiter", 1) + const released = yield* sem.release(1) + const available = yield* sem.available + const reacquired = yield* sem.withPermitsIfAvailable(1)(Effect.succeed("ok")) + return [released, available, reacquired] + }).pipe(Effect.forkChild({ startImmediately: true })) + + yield* sem.release(1) + assert.deepStrictEqual(yield* Fiber.join(waiter), [1, 1, Option.some("ok")]) + assert.strictEqual(yield* sem.available, 1) + })) + + it.effect("drains a long queue of synchronous withPermit tasks", () => + Effect.gen(function*() { + const tasks: Array<() => void> = [] + const scheduler: Scheduler.Scheduler = { + executionMode: "async", + makeDispatcher: () => ({ + scheduleTask: (task) => tasks.push(task), + flush() {} + }), + shouldYield: () => false + } + const sem = yield* PartitionedSemaphore.make({ permits: 1 }) + yield* sem.take("holder", 1) + let completed = 0 + for (let i = 0; i < 10_000; i++) { + // Detach so a stranded waiter cannot hang scope teardown on regression. + yield* sem.withPermit(String(i))(Effect.sync(() => completed++)).pipe( + Effect.provideService(Scheduler.Scheduler, scheduler), + Effect.forkDetach({ startImmediately: true }) + ) + } + assert.strictEqual(completed, 0) + yield* sem.release(1).pipe(Effect.provideService(Scheduler.Scheduler, scheduler)) + while (tasks.length > 0) tasks.shift()!() + assert.strictEqual(completed, 10_000) + assert.strictEqual(yield* sem.available, 1) + })) + it.effect("module-level combinators delegate to the instance api", () => Effect.gen(function*() { const sem = yield* PartitionedSemaphore.make({ permits: 2 }) diff --git a/repos/effect/packages/effect/test/Pathfinding.test.ts b/packages/effect/test/Pathfinding.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Pathfinding.test.ts rename to packages/effect/test/Pathfinding.test.ts diff --git a/repos/effect/packages/effect/test/Pool.test.ts b/packages/effect/test/Pool.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Pool.test.ts rename to packages/effect/test/Pool.test.ts index b3faee601..1dd1a8aeb 100644 --- a/repos/effect/packages/effect/test/Pool.test.ts +++ b/packages/effect/test/Pool.test.ts @@ -47,6 +47,27 @@ describe("Pool", () => { strictEqual(result, 0) })) + it.effect("shutdown waits for borrowed items before releasing owner dependencies", () => + Effect.gen(function*() { + const events: Array = [] + const owner = yield* Scope.make() + const borrower = yield* Scope.make() + yield* Scope.addFinalizer(owner, Effect.sync(() => events.push("owner-dependency-released"))) + const pool = yield* Pool.make({ + size: 1, + acquire: Effect.acquireRelease( + Effect.succeed("resource"), + () => Effect.sync(() => events.push("resource-released")) + ) + }).pipe(Scope.provide(owner)) + yield* Pool.get(pool).pipe(Scope.provide(borrower)) + const closing = yield* Effect.forkChild(Scope.close(owner, Exit.void), { startImmediately: true }) + yield* Effect.yieldNow + yield* Scope.close(borrower, Exit.void) + yield* Fiber.join(closing) + deepStrictEqual(events, ["resource-released", "owner-dependency-released"]) + })) + it.effect("defects don't prevent cleanup", () => Effect.gen(function*() { const count = yield* Ref.make(0) diff --git a/repos/effect/packages/effect/test/Predicate.test.ts b/packages/effect/test/Predicate.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Predicate.test.ts rename to packages/effect/test/Predicate.test.ts diff --git a/repos/effect/packages/effect/test/PubSub.test.ts b/packages/effect/test/PubSub.test.ts similarity index 93% rename from repos/effect/packages/effect/test/PubSub.test.ts rename to packages/effect/test/PubSub.test.ts index 655aac0f5..cb07a1334 100644 --- a/repos/effect/packages/effect/test/PubSub.test.ts +++ b/packages/effect/test/PubSub.test.ts @@ -462,6 +462,15 @@ describe("PubSub", () => { assert.deepStrictEqual(PubSub.sizeUnsafe(pubsub), 0) })) + for (const options of [false, true]) { + it.each([Number.NaN, 0.5, 1.5])( + `makeAtomicBounded rejects invalid capacity %s (options: ${options})`, + (capacity) => { + assert.throws(() => PubSub.makeAtomicBounded(options ? { capacity } : capacity)) + } + ) + } + it("normalizes low-level polling and replay counts", () => { const implementations = [ PubSub.makeAtomicBounded(1), @@ -795,6 +804,39 @@ describe("PubSub", () => { })) describe("end", () => { + // Yield after the empty check, before the subscriber registers its waiter. + it.effect("take delivers data published during waiter registration before the final message", () => + Effect.gen(function*() { + const pubsub = yield* PubSub.unbounded() + const subscription = yield* PubSub.subscribe(pubsub) + const fiber = yield* Effect.forkChild( + PubSub.take(subscription).pipe(Effect.provideService(Scheduler.MaxOpsBeforeYield, 3)), + { startImmediately: true } + ) + PubSub.publishUnsafe(pubsub, 1) + PubSub.endUnsafe(pubsub, 0) + + const first = yield* Fiber.join(fiber) + const last = yield* PubSub.take(subscription) + assert.deepStrictEqual([first, last], [1, 0]) + })) + + it.effect("takeAll delivers data published during waiter registration before the final message", () => + Effect.gen(function*() { + const pubsub = yield* PubSub.unbounded() + const subscription = yield* PubSub.subscribe(pubsub) + const fiber = yield* Effect.forkChild( + PubSub.takeAll(subscription).pipe(Effect.provideService(Scheduler.MaxOpsBeforeYield, 3)), + { startImmediately: true } + ) + PubSub.publishUnsafe(pubsub, 1) + PubSub.endUnsafe(pubsub, 0) + + const first = yield* Fiber.join(fiber) + const last = yield* PubSub.takeAll(subscription) + assert.deepStrictEqual([first, last], [[1], [0]]) + })) + it.effect("delivers buffered messages before the final message and rejects later publishes", () => Effect.gen(function*() { const pubsub = yield* PubSub.bounded(4) @@ -866,6 +908,25 @@ describe("PubSub", () => { assert.strictEqual(yield* PubSub.take(slow), 0) })) + it.effect("rejects a backpressured publish that yields before registering its surplus", () => + Effect.gen(function*() { + const pubsub = yield* PubSub.bounded(1) + const subscription = yield* PubSub.subscribe(pubsub) + yield* PubSub.publish(pubsub, 1) + // Yield after the initial lifecycle check, before surplus registration. + const publisher = yield* Effect.forkChild( + PubSub.publish(pubsub, 2).pipe(Effect.provideService(Scheduler.MaxOpsBeforeYield, 3)), + { startImmediately: true } + ) + + PubSub.endUnsafe(pubsub, 0) + yield* Effect.yieldNow + + assert.strictEqual(yield* PubSub.take(subscription), 1) + assert.strictEqual(yield* PubSub.take(subscription), 0) + assert.isFalse(yield* Fiber.join(publisher)) + })) + it.effect("shutdown still interrupts subscribers", () => Effect.gen(function*() { const pubsub = yield* PubSub.unbounded() diff --git a/repos/effect/packages/effect/test/Pull.test.ts b/packages/effect/test/Pull.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Pull.test.ts rename to packages/effect/test/Pull.test.ts diff --git a/repos/effect/packages/effect/test/Queue.test.ts b/packages/effect/test/Queue.test.ts similarity index 90% rename from repos/effect/packages/effect/test/Queue.test.ts rename to packages/effect/test/Queue.test.ts index 2f90b9b57..6f4beea84 100644 --- a/repos/effect/packages/effect/test/Queue.test.ts +++ b/packages/effect/test/Queue.test.ts @@ -177,7 +177,7 @@ describe("Queue", () => { })) } - it.effect(`resuming ${method} can shut down the queue without defecting the consumer`, () => + it.effect(`resuming ${method} can shut down the queue with another blocked producer`, () => Effect.gen(function*() { const queue = yield* Queue.bounded(1) yield* Queue.offer(queue, 0) @@ -189,11 +189,15 @@ describe("Queue", () => { }), { startImmediately: true } ) + const pending = yield* Effect.forkChild(Queue.offer(queue, 2), { startImmediately: true }) + assert.isUndefined(producer.pollUnsafe(), "first producer must be suspended") + assert.isUndefined(pending.pollUnsafe(), "second producer must be suspended") const exit = yield* Effect.exit(Queue.take(queue)) yield* Fiber.join(producer) + const offered = yield* Fiber.join(pending) - assert.deepStrictEqual(exit, Exit.succeed(0)) + assert.deepStrictEqual({ exit, offered }, { exit: Exit.succeed(0), offered: false }) })) it.effect(`resuming ${method} does not overfill the queue with reentrant offers`, () => @@ -459,6 +463,58 @@ describe("Queue", () => { assert.strictEqual(yield* Fiber.join(liveFiber), 1) })) + // Yielding and interrupting at each yield check stands in for a timeout firing + // while a dequeue yields, including after it has removed the message. + const dequeues: Array<[string, (queue: Queue.Queue) => Effect.Effect>, boolean]> = [ + ["take", (queue) => Effect.map(Queue.take(queue), (value) => [value]), true], + ["takeAll", Queue.takeAll, true], + ["takeN", (queue) => Queue.takeN(queue, 1), true], + ["takeBetween", (queue) => Queue.takeBetween(queue, 1, 2), true], + ["poll", (queue) => Effect.map(Queue.poll(queue), Option.toArray), false], + ["clear", Queue.clear, false] + ] + for (const [name, dequeue, waits] of dequeues) { + it.effect.each([ + { capacity: Infinity, offerFirst: true }, + { capacity: Infinity, offerFirst: false }, + { capacity: 0, offerFirst: true }, + { capacity: 0, offerFirst: false } + ].filter(({ offerFirst }) => waits || offerFirst))( + `${name} interrupted at a yield keeps its message, capacity=$capacity offerFirst=$offerFirst`, + ({ capacity, offerFirst }) => + Effect.gen(function*() { + let at = 0 + let seen: number + do { + at++ + seen = 0 + const base = new Scheduler.MixedScheduler() + const scheduler: Scheduler.Scheduler = { + executionMode: base.executionMode, + makeDispatcher: () => base.makeDispatcher(), + shouldYield: (fiber) => { + if (++seen !== at) return base.shouldYield(fiber) + // Interrupt before the scheduler resumes the fiber. + queueMicrotask(() => fiber.interruptUnsafe()) + return true + } + } + const queue = yield* Queue.make({ capacity }) + const offer = Effect.forkChild(Queue.offer(queue, 1), { startImmediately: true }) + if (offerFirst) yield* offer + const taker = yield* dequeue(queue).pipe( + Effect.provideService(Scheduler.Scheduler, scheduler), + Effect.forkChild({ startImmediately: true }) + ) + if (!offerFirst) yield* offer + const exit = yield* Fiber.await(taker) + const taken = Exit.isSuccess(exit) ? exit.value : [] + assert.deepStrictEqual([...taken, ...yield* Queue.clear(queue)], [1], `interrupted at yield check ${at}`) + } while (seen >= at) + }) + ) + } + it.effect("done completes takes", () => Effect.gen(function*() { const queue = yield* Queue.bounded(2) @@ -847,6 +903,14 @@ describe("Queue", () => { assert.strictEqual(next, 2) })) + it.effect("clear preserves a defect merged with Done", () => + Effect.gen(function*() { + const queue = yield* Queue.unbounded() + yield* Queue.failCause(queue, Cause.combine(Cause.fail(Cause.Done()), Cause.die("finalizer boom"))) + + assert.deepStrictEqual(yield* Effect.exit(Queue.clear(queue)), Exit.die("finalizer boom")) + })) + it.effect("zero-capacity offerAll drains in order and completes a closing queue", () => Effect.gen(function*() { const queue = yield* Queue.bounded(0) diff --git a/repos/effect/packages/effect/test/Random.test.ts b/packages/effect/test/Random.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Random.test.ts rename to packages/effect/test/Random.test.ts diff --git a/repos/effect/packages/effect/test/RcMap.test.ts b/packages/effect/test/RcMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/RcMap.test.ts rename to packages/effect/test/RcMap.test.ts diff --git a/repos/effect/packages/effect/test/RcRef.test.ts b/packages/effect/test/RcRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/RcRef.test.ts rename to packages/effect/test/RcRef.test.ts diff --git a/repos/effect/packages/effect/test/Record.test.ts b/packages/effect/test/Record.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Record.test.ts rename to packages/effect/test/Record.test.ts diff --git a/repos/effect/packages/effect/test/Redacted.test.ts b/packages/effect/test/Redacted.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Redacted.test.ts rename to packages/effect/test/Redacted.test.ts diff --git a/repos/effect/packages/effect/test/Reducer.test.ts b/packages/effect/test/Reducer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Reducer.test.ts rename to packages/effect/test/Reducer.test.ts diff --git a/repos/effect/packages/effect/test/Ref.test.ts b/packages/effect/test/Ref.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Ref.test.ts rename to packages/effect/test/Ref.test.ts diff --git a/repos/effect/packages/effect/test/Request.test.ts b/packages/effect/test/Request.test.ts similarity index 96% rename from repos/effect/packages/effect/test/Request.test.ts rename to packages/effect/test/Request.test.ts index cc6d9911c..05ca5b26f 100644 --- a/repos/effect/packages/effect/test/Request.test.ts +++ b/packages/effect/test/Request.test.ts @@ -424,6 +424,29 @@ describe("Request", { concurrent: false }, () => { })) } + it.effect("withCache completes many coalesced requests for one cache entry", () => + Effect.gen(function*() { + const gate = yield* Deferred.make() + const batches: Array = [] + const resolver = yield* Resolver.make((entries) => + Effect.sync(() => { + batches.push(entries.length) + for (const entry of entries) entry.completeUnsafe(Exit.succeed("Alice")) + }) + ).pipe(Resolver.setDelayEffect(Deferred.await(gate)), Resolver.withCache({ capacity: 1 })) + const fibers = yield* Effect.forEach( + Array.range(1, 20_000), + () => Effect.request(new GetNameById({ id: 1 }), resolver).pipe(Effect.forkChild({ startImmediately: true })) + ) + yield* Deferred.succeed(gate, undefined) + yield* Effect.yieldNow + + assert.deepStrictEqual(batches, [1]) + for (const fiber of fibers) { + assert.deepStrictEqual(fiber.pollUnsafe(), Exit.succeed("Alice")) + } + })) + it.effect("withCache still caches completed failures", () => Effect.gen(function*() { let calls = 0 diff --git a/repos/effect/packages/effect/test/Resource.test.ts b/packages/effect/test/Resource.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Resource.test.ts rename to packages/effect/test/Resource.test.ts diff --git a/repos/effect/packages/effect/test/Result.test.ts b/packages/effect/test/Result.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Result.test.ts rename to packages/effect/test/Result.test.ts diff --git a/repos/effect/packages/effect/test/Schedule.test.ts b/packages/effect/test/Schedule.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Schedule.test.ts rename to packages/effect/test/Schedule.test.ts diff --git a/repos/effect/packages/effect/test/Scheduler.test.ts b/packages/effect/test/Scheduler.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Scheduler.test.ts rename to packages/effect/test/Scheduler.test.ts diff --git a/repos/effect/packages/effect/test/Scope.test.ts b/packages/effect/test/Scope.test.ts similarity index 92% rename from repos/effect/packages/effect/test/Scope.test.ts rename to packages/effect/test/Scope.test.ts index 73dccd0d8..9b3becdbc 100644 --- a/repos/effect/packages/effect/test/Scope.test.ts +++ b/packages/effect/test/Scope.test.ts @@ -19,6 +19,23 @@ describe("Scope", () => { describe("interrupted close", () => { for (const strategy of ["sequential", "parallel"] as const) { + it(strategy + ": completes a single finalizer when its callback interrupts close", async () => { + const controller = new AbortController() + const scope = Scope.makeUnsafe(strategy) + let cleaned = false + Effect.runSync(Scope.addFinalizerExit(scope, () => { + controller.abort() + return Effect.sync(() => { + cleaned = true + }) + })) + // Yield so the runner installs its abort listener before the callback runs. + await Effect.runPromiseExit(Effect.andThen(Effect.yieldNow, Scope.close(scope, Exit.void)), { + signal: controller.signal + }) + expect(cleaned).toBe(true) + }) + it.effect(strategy + ": completes direct close after interruption", () => Effect.gen(function*() { const scope = Scope.makeUnsafe(strategy) diff --git a/repos/effect/packages/effect/test/ScopedCache.test.ts b/packages/effect/test/ScopedCache.test.ts similarity index 99% rename from repos/effect/packages/effect/test/ScopedCache.test.ts rename to packages/effect/test/ScopedCache.test.ts index 961afbf93..c6c312836 100644 --- a/repos/effect/packages/effect/test/ScopedCache.test.ts +++ b/packages/effect/test/ScopedCache.test.ts @@ -340,6 +340,33 @@ describe("ScopedCache", () => { assert.strictEqual(result3, 2) })) + it.effect("get succeeds after an expired entry's finalizer dies", () => + Effect.gen(function*() { + let lookups = 0 + const cache = yield* ScopedCache.make({ + capacity: 2, + timeToLive: 0, + lookup: (_key: string) => + Effect.acquireRelease( + Effect.sync(() => ++lookups), + (value) => value === 1 ? Effect.die("cleanup defect") : Effect.void + ) + }) + + assert.strictEqual(yield* ScopedCache.get(cache, "key"), 1) + + for (const expected of [2, 3]) { + const reader = yield* ScopedCache.get(cache, "key").pipe( + Effect.exit, + Effect.timeoutOption("1 second"), + Effect.forkChild + ) + yield* TestClock.adjust("1 second") + + assert.deepStrictEqual(yield* Fiber.join(reader), Option.some(Exit.succeed(expected))) + } + })) + it.effect("error handling - lookup function fails", () => Effect.gen(function*() { const cache = yield* ScopedCache.make({ diff --git a/repos/effect/packages/effect/test/ScopedRef.test.ts b/packages/effect/test/ScopedRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ScopedRef.test.ts rename to packages/effect/test/ScopedRef.test.ts diff --git a/repos/effect/packages/effect/test/Semaphore.test.ts b/packages/effect/test/Semaphore.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Semaphore.test.ts rename to packages/effect/test/Semaphore.test.ts diff --git a/repos/effect/packages/effect/test/Sink.test.ts b/packages/effect/test/Sink.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Sink.test.ts rename to packages/effect/test/Sink.test.ts index a63091c78..39c9e1a24 100644 --- a/repos/effect/packages/effect/test/Sink.test.ts +++ b/packages/effect/test/Sink.test.ts @@ -13,6 +13,13 @@ import { TestClock } from "effect/testing" describe("Sink", () => { describe("constructors", () => { + it.effect("make uses the pipeline success value as the sink result", () => + Effect.gen(function*() { + const sink = Sink.make()(Stream.runSum) + const exit = yield* Effect.exit(Stream.run(Stream.make(1, 2, 3), sink)) + assertExitSuccess(exit, 6) + })) + it.effect("fromWritableStream - aborts instead of closing on upstream failure", () => Effect.gen(function*() { const error = new Error("upstream failed") diff --git a/repos/effect/packages/effect/test/StackCapture.test.ts b/packages/effect/test/StackCapture.test.ts similarity index 100% rename from repos/effect/packages/effect/test/StackCapture.test.ts rename to packages/effect/test/StackCapture.test.ts diff --git a/repos/effect/packages/effect/test/StackTraceLimit.test.ts b/packages/effect/test/StackTraceLimit.test.ts similarity index 100% rename from repos/effect/packages/effect/test/StackTraceLimit.test.ts rename to packages/effect/test/StackTraceLimit.test.ts diff --git a/repos/effect/packages/effect/test/Stdio.test.ts b/packages/effect/test/Stdio.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Stdio.test.ts rename to packages/effect/test/Stdio.test.ts diff --git a/repos/effect/packages/effect/test/Stream.test.ts b/packages/effect/test/Stream.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Stream.test.ts rename to packages/effect/test/Stream.test.ts index e6933ce98..dc4b64634 100644 --- a/repos/effect/packages/effect/test/Stream.test.ts +++ b/packages/effect/test/Stream.test.ts @@ -1425,6 +1425,28 @@ describe("Stream", () => { ["even", [2, 4]] ]) })) + + it.effect("groupByKey - a group that stops early does not block the others", () => + Effect.gen(function*() { + const result = yield* Stream.range(1, 8).pipe( + Stream.rechunk(1), + Stream.groupByKey((n) => n % 2, { bufferSize: 1 }), + Stream.flatMap(([key, group]) => key === 0 ? group : Stream.take(group, 1), { concurrency: "unbounded" }), + Stream.runCollect + ) + assert.deepStrictEqual(result.sort((a, b) => a - b), [1, 2, 4, 6, 8]) + })) + + it.effect("groupBy - a group that stops early does not block the others", () => + Effect.gen(function*() { + const result = yield* Stream.range(1, 8).pipe( + Stream.rechunk(1), + Stream.groupBy((n) => Effect.succeed([n % 2, n] as const), { bufferSize: 1 }), + Stream.flatMap(([key, group]) => key === 0 ? group : Stream.take(group, 1), { concurrency: "unbounded" }), + Stream.runCollect + ) + assert.deepStrictEqual(result.sort((a, b) => a - b), [1, 2, 4, 6, 8]) + })) }) describe("concat", () => { @@ -3937,6 +3959,17 @@ describe("Stream", () => { }) describe("zipLatest", () => { + for (const emptySide of ["left", "right"] as const) { + it.effect(`completes with no pairs when the ${emptySide} stream is empty`, () => + Effect.gen(function*() { + const result = yield* Stream.zipLatest( + emptySide === "left" ? Stream.empty : Stream.succeed(1), + emptySide === "right" ? Stream.empty : Stream.succeed(1) + ).pipe(Stream.runCollect) + assert.deepStrictEqual(result, []) + })) + } + it.effect("combines streams with latest values", () => Effect.gen(function*() { const result = yield* Stream.zipLatest( @@ -4179,6 +4212,22 @@ describe("Stream", () => { deepStrictEqual(result, [1, 1, 2, 3, 5, 8]) })) + it.live("sink finishing during an upstream pull still lets the stream complete", () => + Effect.gen(function*() { + const started = yield* Deferred.make() + const finished = yield* Deferred.make() + const source = Stream.fromEffect(Effect.gen(function*() { + yield* Deferred.succeed(started, undefined) + yield* Deferred.await(finished) + // Let the sink finish before the suspended upstream pull returns. + yield* Effect.yieldNow + return 1 + })) + const sink = Sink.fromEffect(Effect.andThen(Deferred.await(started), Deferred.succeed(finished, undefined))) + const result = yield* source.pipe(Stream.tapSink(sink), Stream.runCollect, Effect.timeoutOption("1 second")) + deepStrictEqual(result, Option.some([1])) + })) + it.effect("sink that fails before stream", () => Effect.gen(function*() { const sink = Sink.fail("error") @@ -4191,6 +4240,13 @@ describe("Stream", () => { strictEqual(result, "error") })) + it.effect("sink that fails after end-of-stream", () => + Effect.gen(function*() { + const sink = Sink.collect().pipe(Sink.mapEffect(() => Effect.fail("sink-end-failure"))) + const exit = yield* Stream.make(1).pipe(Stream.tapSink(sink), Stream.runCollect, Effect.exit) + deepStrictEqual(exit, Exit.fail("sink-end-failure")) + })) + it.effect("does not read ahead", () => Effect.gen(function*() { const ref = yield* Ref.make(0) @@ -4581,6 +4637,18 @@ describe("Stream", () => { deepStrictEqual(result2, [1, 3, 5]) })) + it.effect("one side stopping early does not block the other", () => + Effect.gen(function*() { + const [evens, odds] = yield* Stream.range(1, 8).pipe( + Stream.partition((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n), { capacity: 1 }) + ) + const result = yield* Effect.all([ + Stream.runCollect(evens), + Stream.runCollect(Stream.take(odds, 1)) + ], { concurrency: 2 }) + deepStrictEqual(result, [[2, 4, 6, 8], [1]]) + }).pipe(Effect.scoped)) + it.effect("errors", () => Effect.gen(function*() { const { result1, result2 } = yield* pipe( @@ -4674,6 +4742,20 @@ describe("Stream", () => { deepStrictEqual(result2, ["odd:1", "odd:3", "odd:5"]) })) + it.effect("partitionEffect - one side stopping early does not block the other", () => + Effect.gen(function*() { + const [evens, odds] = yield* Stream.range(1, 8).pipe( + Stream.partitionEffect((n) => Effect.succeed(n % 2 === 0 ? Result.succeed(n) : Result.fail(n)), { + capacity: 1 + }) + ) + const result = yield* Effect.all([ + Stream.runCollect(evens), + Stream.runCollect(Stream.take(odds, 1)) + ], { concurrency: 2 }) + deepStrictEqual(result, [[2, 4, 6, 8], [1]]) + }).pipe(Effect.scoped)) + it.effect("partitionQueue - values", () => Effect.gen(function*() { const { result1, result2 } = yield* pipe( @@ -4803,6 +4885,13 @@ describe("Stream", () => { deepStrictEqual(rest, [4, 5, 6]) })) + it.effect("keeps the sink's leftovers in the remaining stream", () => + Effect.gen(function*() { + const [peeled, rest] = yield* Stream.peel(Stream.make(1, 2, 3, 4), Sink.take(2)) + deepStrictEqual(peeled, [1, 2]) + deepStrictEqual(yield* Stream.runCollect(rest), [3, 4]) + }).pipe(Effect.scoped)) + it.effect("peel - propagates errors", () => Effect.gen(function*() { const stream = Stream.fromEffectRepeat(Effect.fail("fail")) @@ -5286,6 +5375,16 @@ describe("Stream", () => { ]) })) + it.effect("slidingSize keeps all elements of the final partial window", () => + Effect.gen(function*() { + const result = yield* Stream.make(1, 2, 3, 4, 5, 6).pipe( + Stream.slidingSize(3, 2), + Stream.runCollect + ) + + deepStrictEqual(result, [[1, 2, 3], [3, 4, 5], [5, 6]]) + })) + it.effect("sliding - fails if upstream produces an error", () => Effect.gen(function*() { const result = yield* pipe( diff --git a/repos/effect/packages/effect/test/String.test.ts b/packages/effect/test/String.test.ts similarity index 100% rename from repos/effect/packages/effect/test/String.test.ts rename to packages/effect/test/String.test.ts diff --git a/repos/effect/packages/effect/test/Struct.test.ts b/packages/effect/test/Struct.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Struct.test.ts rename to packages/effect/test/Struct.test.ts diff --git a/repos/effect/packages/effect/test/SubscriptionRef.test.ts b/packages/effect/test/SubscriptionRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SubscriptionRef.test.ts rename to packages/effect/test/SubscriptionRef.test.ts diff --git a/repos/effect/packages/effect/test/Symbol.test.ts b/packages/effect/test/Symbol.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Symbol.test.ts rename to packages/effect/test/Symbol.test.ts diff --git a/repos/effect/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts b/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts rename to packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts diff --git a/repos/effect/packages/effect/test/SynchronizedRef.test.ts b/packages/effect/test/SynchronizedRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SynchronizedRef.test.ts rename to packages/effect/test/SynchronizedRef.test.ts diff --git a/repos/effect/packages/effect/test/TestClock.test.ts b/packages/effect/test/TestClock.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TestClock.test.ts rename to packages/effect/test/TestClock.test.ts diff --git a/repos/effect/packages/effect/test/Tracer.test.ts b/packages/effect/test/Tracer.test.ts similarity index 87% rename from repos/effect/packages/effect/test/Tracer.test.ts rename to packages/effect/test/Tracer.test.ts index 899f3c80a..68e2eb3b1 100644 --- a/repos/effect/packages/effect/test/Tracer.test.ts +++ b/packages/effect/test/Tracer.test.ts @@ -1,7 +1,7 @@ import { assert, describe, it } from "@effect/vitest" import { assertNone, deepStrictEqual, strictEqual } from "@effect/vitest/utils" import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, Scheduler, Tracer } from "effect" -import { HttpClient, HttpClientResponse } from "effect/http" +import { HttpBody, HttpClient, HttpClientResponse } from "effect/http" import { OtlpSerialization, OtlpTracer } from "effect/observability" import { TestClock } from "effect/testing" import type { Span } from "effect/Tracer" @@ -13,18 +13,21 @@ const getParent = (span: Tracer.Span): Tracer.AnySpan => { return span.parent.value } -const otlpTracerLayer = OtlpTracer.layer({ - url: "http://localhost:4318/v1/traces", - resource: { - serviceName: "test-service" - } -}).pipe( - Layer.provide(OtlpSerialization.layerJson), - Layer.provide(Layer.succeed( - HttpClient.HttpClient, - HttpClient.make((request) => Effect.succeed(HttpClientResponse.fromWeb(request, new Response()))) - )) -) +const makeOtlpTracerLayer = (serialization: Layer.Layer) => + OtlpTracer.layer({ + url: "http://localhost:4318/v1/traces", + resource: { + serviceName: "test-service" + } + }).pipe( + Layer.provide(serialization), + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + HttpClient.make((request) => Effect.succeed(HttpClientResponse.fromWeb(request, new Response()))) + )) + ) + +const otlpTracerLayer = makeOtlpTracerLayer(OtlpSerialization.layerJson) const makeRootAndChildSpans = Effect.gen(function*() { const root = yield* Effect.withSpan(Effect.currentSpan, "root") @@ -57,6 +60,19 @@ describe("Tracer", () => { }) describe("Effect.withSpan", () => { + it.effect("should capture only the span frame for a string failure", () => + Effect.gen(function*() { + const cause = yield* Effect.fail("boom").pipe( + Effect.withSpan("test"), + Effect.sandbox, + Effect.flip + ) + + const errors = Cause.prettyErrors(cause) + assert.lengthOf(errors, 1) + assert.match(errors[0].stack!, /^Error: boom\n at test \(.*Tracer\.test\.ts:\d+:\d+\)$/) + })) + it.effect("should capture the stack trace", () => Effect.gen(function*() { const error = new Error("boom") @@ -324,6 +340,49 @@ describe("Tracer", () => { }) }) + describe("OtlpTracer span status", () => { + const exportSpan = (exit: Exit.Exit) => { + const exported: Array = [] + const layer = makeOtlpTracerLayer(Layer.succeed(OtlpSerialization.OtlpSerialization, { + traces: (data) => { + exported.push(data) + return HttpBody.empty + }, + metrics: () => HttpBody.empty, + logs: () => HttpBody.empty + })) + return Effect.gen(function*() { + const span = yield* Effect.makeSpan("test") + span.end(span.status.startTime + 1n, exit) + }).pipe( + Effect.provide(layer), + Effect.map(() => { + const spans = exported.flatMap((data) => + data.resourceSpans.flatMap((resource) => resource.scopeSpans.flatMap((scope) => scope.spans)) + ) + assert.lengthOf(spans, 1) + return spans[0] + }) + ) + } + + it.effect("marks an empty cause Ok without a description", () => + Effect.gen(function*() { + const span = yield* exportSpan(Exit.failCause(Cause.empty)) + deepStrictEqual(span.status, { code: 1 }) + deepStrictEqual(span.events, []) + deepStrictEqual(span.attributes, []) + })) + + it.effect("leaves interruption Unset with effect.fiber.interrupted", () => + Effect.gen(function*() { + const span = yield* exportSpan(Exit.interrupt()) + deepStrictEqual(span.status, { code: 0 }) + deepStrictEqual(span.events, []) + deepStrictEqual(span.attributes, [{ key: "effect.fiber.interrupted", value: { boolValue: true } }]) + })) + }) + describe("Effect.useSpanScoped", () => { it.effect("should control span lifetimes with a scope", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/Trie.test.ts b/packages/effect/test/Trie.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Trie.test.ts rename to packages/effect/test/Trie.test.ts diff --git a/repos/effect/packages/effect/test/Tuple.test.ts b/packages/effect/test/Tuple.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Tuple.test.ts rename to packages/effect/test/Tuple.test.ts diff --git a/repos/effect/packages/effect/test/TxChunk.test.ts b/packages/effect/test/TxChunk.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxChunk.test.ts rename to packages/effect/test/TxChunk.test.ts diff --git a/repos/effect/packages/effect/test/TxDeferred.test.ts b/packages/effect/test/TxDeferred.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxDeferred.test.ts rename to packages/effect/test/TxDeferred.test.ts diff --git a/repos/effect/packages/effect/test/TxHashMap.test.ts b/packages/effect/test/TxHashMap.test.ts similarity index 92% rename from repos/effect/packages/effect/test/TxHashMap.test.ts rename to packages/effect/test/TxHashMap.test.ts index bd4a5b132..a1469795a 100644 --- a/repos/effect/packages/effect/test/TxHashMap.test.ts +++ b/packages/effect/test/TxHashMap.test.ts @@ -1,8 +1,28 @@ import { assert, describe, it } from "@effect/vitest" -import { Effect, Hash, HashMap, Option, Result, TxHashMap } from "effect" +import { Effect, Fiber, Hash, HashMap, Option, Result, TxHashMap, TxRef } from "effect" describe("TxHashMap", () => { describe("constructors", () => { + it.effect("fromIterable preserves a one-shot iterable across transaction retries", () => + Effect.gen(function*() { + const gate = yield* TxRef.make(false) + const operation = TxHashMap.fromIterable((function*() { + yield ["a", 1] as const + yield ["b", 2] as const + })()) + const fiber = yield* Effect.forkChild( + Effect.tx(Effect.gen(function*() { + const txMap = yield* operation + if (!(yield* TxRef.get(gate))) return yield* Effect.txRetry + return txMap + })), + { startImmediately: true } + ) + yield* TxRef.set(gate, true) + const txMap = yield* Fiber.join(fiber) + assert.deepStrictEqual((yield* TxHashMap.toEntries(txMap)).sort(), [["a", 1], ["b", 2]]) + })) + it.effect("empty", () => Effect.tx(Effect.gen(function*() { const txMap = yield* TxHashMap.empty() @@ -27,6 +47,20 @@ describe("TxHashMap", () => { assert.deepStrictEqual(c, Option.some(3)) }))) + it.effect("fromIterable creates independent maps on each execution", () => + Effect.gen(function*() { + const make = TxHashMap.fromIterable((function*() { + yield ["a", 1] as const + })()) + const first = yield* Effect.tx(make) + yield* Effect.tx(TxHashMap.clear(first)) + const second = yield* Effect.tx(make) + + assert.strictEqual(yield* Effect.tx(TxHashMap.size(second)), 1) + assert.deepStrictEqual(yield* Effect.tx(TxHashMap.get(second, "a")), Option.some(1)) + assert.strictEqual(yield* Effect.tx(TxHashMap.size(first)), 0) + })) + it.effect("fromIterable", () => Effect.tx(Effect.gen(function*() { const entries = [["a", 1], ["b", 2], ["c", 3]] as const @@ -279,6 +313,29 @@ describe("TxHashMap", () => { assert.deepStrictEqual(c, Option.some(3)) }))) + it.effect("removeMany preserves a one-shot iterable across transaction retries", () => + Effect.gen(function*() { + const txMap = yield* TxHashMap.make(["a", 1], ["b", 2], ["c", 3]) + const gate = yield* TxRef.make(false) + const operation = TxHashMap.removeMany( + txMap, + (function*() { + yield "a" + yield "b" + })() + ) + const fiber = yield* Effect.forkChild( + Effect.tx(Effect.gen(function*() { + yield* operation + if (!(yield* TxRef.get(gate))) return yield* Effect.txRetry + })), + { startImmediately: true } + ) + yield* TxRef.set(gate, true) + yield* Fiber.join(fiber) + assert.deepStrictEqual(yield* TxHashMap.toEntries(txMap), [["c", 3]]) + })) + it.effect("removeMany", () => Effect.tx(Effect.gen(function*() { const txMap = yield* TxHashMap.make(["a", 1], ["b", 2], ["c", 3], ["d", 4]) @@ -297,6 +354,29 @@ describe("TxHashMap", () => { assert.strictEqual(hasD, false) }))) + it.effect("setMany preserves a one-shot iterable across transaction retries", () => + Effect.gen(function*() { + const txMap = yield* TxHashMap.make(["a", 0], ["c", 3]) + const gate = yield* TxRef.make(false) + const operation = TxHashMap.setMany( + txMap, + (function*() { + yield ["a", 1] as const + yield ["b", 2] as const + })() + ) + const fiber = yield* Effect.forkChild( + Effect.tx(Effect.gen(function*() { + yield* operation + if (!(yield* TxRef.get(gate))) return yield* Effect.txRetry + })), + { startImmediately: true } + ) + yield* TxRef.set(gate, true) + yield* Fiber.join(fiber) + assert.deepStrictEqual((yield* TxHashMap.toEntries(txMap)).sort(), [["a", 1], ["b", 2], ["c", 3]]) + })) + it.effect("setMany", () => Effect.tx(Effect.gen(function*() { const txMap = yield* TxHashMap.make(["a", 1], ["b", 2]) diff --git a/repos/effect/packages/effect/test/TxHashSet.test.ts b/packages/effect/test/TxHashSet.test.ts similarity index 92% rename from repos/effect/packages/effect/test/TxHashSet.test.ts rename to packages/effect/test/TxHashSet.test.ts index 674c95083..44f0a909f 100644 --- a/repos/effect/packages/effect/test/TxHashSet.test.ts +++ b/packages/effect/test/TxHashSet.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Effect, Equal, Hash, HashSet, TxHashSet } from "effect" +import { Effect, Equal, Fiber, Hash, HashSet, TxHashSet, TxRef } from "effect" class TestValue implements Equal.Equal { constructor(readonly value: string) {} @@ -19,6 +19,27 @@ class TestValue implements Equal.Equal { describe("TxHashSet", () => { describe("constructors", () => { + it.effect("fromIterable preserves a one-shot iterable across transaction retries", () => + Effect.gen(function*() { + const gate = yield* TxRef.make(false) + const operation = TxHashSet.fromIterable((function*() { + yield "a" + yield "b" + yield "a" + })()) + const fiber = yield* Effect.forkChild( + Effect.tx(Effect.gen(function*() { + const txSet = yield* operation + if (!(yield* TxRef.get(gate))) return yield* Effect.txRetry + return txSet + })), + { startImmediately: true } + ) + yield* TxRef.set(gate, true) + const txSet = yield* Fiber.join(fiber) + assert.deepStrictEqual(Array.from(yield* TxHashSet.toHashSet(txSet)).sort(), ["a", "b"]) + })) + it.effect("empty creates an empty TxHashSet", () => Effect.tx(Effect.gen(function*() { const txSet = yield* TxHashSet.empty() @@ -60,6 +81,20 @@ describe("TxHashSet", () => { assert.strictEqual(yield* TxHashSet.has(txSet, "z"), true) }))) + it.effect("fromIterable creates independent sets on each execution", () => + Effect.gen(function*() { + const make = TxHashSet.fromIterable((function*() { + yield "a" + })()) + const first = yield* Effect.tx(make) + yield* Effect.tx(TxHashSet.clear(first)) + const second = yield* Effect.tx(make) + + assert.strictEqual(yield* Effect.tx(TxHashSet.size(second)), 1) + assert.strictEqual(yield* Effect.tx(TxHashSet.has(second, "a")), true) + assert.strictEqual(yield* Effect.tx(TxHashSet.size(first)), 0) + })) + it.effect("fromHashSet creates TxHashSet from HashSet", () => Effect.tx(Effect.gen(function*() { const hashSet = HashSet.make("foo", "bar", "baz") diff --git a/repos/effect/packages/effect/test/TxPriorityQueue.test.ts b/packages/effect/test/TxPriorityQueue.test.ts similarity index 91% rename from repos/effect/packages/effect/test/TxPriorityQueue.test.ts rename to packages/effect/test/TxPriorityQueue.test.ts index dbc4702e6..660d4efc5 100644 --- a/repos/effect/packages/effect/test/TxPriorityQueue.test.ts +++ b/packages/effect/test/TxPriorityQueue.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Effect, Fiber, Option, Order, TxPriorityQueue } from "effect" +import { Effect, Fiber, Option, Order, TxPriorityQueue, TxRef } from "effect" describe("TxPriorityQueue", () => { describe("constructors", () => { @@ -77,6 +77,29 @@ describe("TxPriorityQueue", () => { }) describe("mutations", () => { + it.effect("offerAll preserves a one-shot iterable across transaction retries", () => + Effect.gen(function*() { + const queue = yield* TxPriorityQueue.empty(Order.Number) + const gate = yield* TxRef.make(false) + const offer = TxPriorityQueue.offerAll( + queue, + (function*() { + yield 2 + yield 1 + })() + ) + const fiber = yield* Effect.forkChild( + Effect.tx(Effect.gen(function*() { + yield* offer + if (!(yield* TxRef.get(gate))) return yield* Effect.txRetry + })), + { startImmediately: true } + ) + yield* TxRef.set(gate, true) + yield* Fiber.join(fiber) + assert.deepStrictEqual(yield* TxPriorityQueue.toArray(queue), [1, 2]) + })) + it.effect("offer + take returns elements in priority order", () => Effect.tx(Effect.gen(function*() { const pq = yield* TxPriorityQueue.empty(Order.Number) diff --git a/repos/effect/packages/effect/test/TxPubSub.test.ts b/packages/effect/test/TxPubSub.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxPubSub.test.ts rename to packages/effect/test/TxPubSub.test.ts diff --git a/repos/effect/packages/effect/test/TxQueue.test.ts b/packages/effect/test/TxQueue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxQueue.test.ts rename to packages/effect/test/TxQueue.test.ts diff --git a/repos/effect/packages/effect/test/TxReentrantLock.test.ts b/packages/effect/test/TxReentrantLock.test.ts similarity index 91% rename from repos/effect/packages/effect/test/TxReentrantLock.test.ts rename to packages/effect/test/TxReentrantLock.test.ts index c9884240d..30708a6e0 100644 --- a/repos/effect/packages/effect/test/TxReentrantLock.test.ts +++ b/packages/effect/test/TxReentrantLock.test.ts @@ -2,6 +2,38 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, Exit, Fiber, Scope, TxReentrantLock } from "effect" describe("TxReentrantLock", () => { + describe("interruption while waiting", () => { + const acquisitions = [ + ["withWriteLock", (lock: TxReentrantLock.TxReentrantLock) => TxReentrantLock.withWriteLock(lock, Effect.void)], + ["writeLock", (lock: TxReentrantLock.TxReentrantLock) => Effect.scoped(TxReentrantLock.writeLock(lock))] + ] as const + + for (const [name, acquire] of acquisitions) { + it.effect(name + " can be interrupted while another fiber holds the write lock", () => + Effect.gen(function*() { + const lock = yield* TxReentrantLock.make() + yield* TxReentrantLock.acquireWrite(lock) + + const waiter = yield* Effect.forkChild(acquire(lock), { startImmediately: true }) + const interruptor = yield* Effect.forkChild(Fiber.interrupt(waiter), { startImmediately: true }) + yield* Effect.yieldNow + yield* Effect.yieldNow + + const interrupted = interruptor.pollUnsafe() !== undefined + const writeLocks = yield* TxReentrantLock.writeLocks(lock) + // Release from the owning fiber before asserting, even on the buggy implementation. + yield* TxReentrantLock.releaseWrite(lock) + yield* Fiber.join(interruptor) + + assert.isTrue(interrupted, "interruption must complete before the holder releases") + assert.strictEqual(writeLocks, 1) + assert.isTrue(Exit.hasInterrupts(yield* Fiber.await(waiter))) + assert.strictEqual(yield* TxReentrantLock.readLocks(lock), 0) + assert.strictEqual(yield* TxReentrantLock.writeLocks(lock), 0) + })) + } + }) + describe("constructors", () => { it.effect("make creates an unlocked lock", () => Effect.tx(Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/TxSemaphore.test.ts b/packages/effect/test/TxSemaphore.test.ts similarity index 87% rename from repos/effect/packages/effect/test/TxSemaphore.test.ts rename to packages/effect/test/TxSemaphore.test.ts index 1e903272e..a80861070 100644 --- a/repos/effect/packages/effect/test/TxSemaphore.test.ts +++ b/packages/effect/test/TxSemaphore.test.ts @@ -235,6 +235,41 @@ describe("TxSemaphore", () => { }) describe("concurrency", () => { + const acquisitions = [ + ["withPermits", (semaphore: TxSemaphore.TxSemaphore) => TxSemaphore.withPermits(semaphore, 2, Effect.void)], + [ + "withPermitScoped", + (semaphore: TxSemaphore.TxSemaphore) => Effect.scoped(TxSemaphore.withPermitScoped(semaphore)) + ] + ] as const + + for (const [name, acquire] of acquisitions) { + it.effect( + name + " can be interrupted while waiting for permits", + () => + Effect.gen(function*() { + const semaphore = yield* TxSemaphore.make(2) + yield* TxSemaphore.acquireN(semaphore, 2) + + const waiter = yield* Effect.forkChild(acquire(semaphore), { startImmediately: true }) + const interruptor = yield* Effect.forkChild(Fiber.interrupt(waiter), { startImmediately: true }) + yield* Effect.yieldNow + yield* Effect.yieldNow + + const interrupted = interruptor.pollUnsafe() !== undefined + const available = yield* TxSemaphore.available(semaphore) + // Release before asserting so a masked waiter cannot hang test cleanup. + yield* TxSemaphore.releaseN(semaphore, 2) + yield* Fiber.join(interruptor) + + assert.isTrue(interrupted, "interruption must complete before the holder releases") + assert.strictEqual(available, 0) + assert.isTrue(Exit.hasInterrupts(yield* Fiber.await(waiter))) + assert.strictEqual(yield* TxSemaphore.available(semaphore), 2) + }) + ) + } + it.effect("withPermit bounds concurrent work and releases for the next waiter", () => Effect.gen(function*() { const semaphore = yield* Effect.tx(TxSemaphore.make(3)) diff --git a/repos/effect/packages/effect/test/TxSubscriptionRef.test.ts b/packages/effect/test/TxSubscriptionRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxSubscriptionRef.test.ts rename to packages/effect/test/TxSubscriptionRef.test.ts diff --git a/repos/effect/packages/effect/test/UndefinedOr.test.ts b/packages/effect/test/UndefinedOr.test.ts similarity index 100% rename from repos/effect/packages/effect/test/UndefinedOr.test.ts rename to packages/effect/test/UndefinedOr.test.ts diff --git a/packages/effect/test/Version.test.ts b/packages/effect/test/Version.test.ts new file mode 100644 index 000000000..381ff7ecc --- /dev/null +++ b/packages/effect/test/Version.test.ts @@ -0,0 +1,17 @@ +import { assert, describe, it } from "@effect/vitest" +import * as Version from "effect/Version" + +describe("Version", () => { + it("exposes a real version and supports isolated version overrides", () => { + const original = Version.getCurrentVersion() + assert.match(original, /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/) + assert.notStrictEqual(original, "0.0.0") + try { + Version.setCurrentVersion("test-version") + assert.strictEqual(Version.getCurrentVersion(), "test-version") + } finally { + Version.setCurrentVersion(original) + } + assert.strictEqual(Version.getCurrentVersion(), original) + }) +}) diff --git a/repos/effect/packages/effect/test/ai/AiError.test.ts b/packages/effect/test/ai/AiError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/AiError.test.ts rename to packages/effect/test/ai/AiError.test.ts diff --git a/repos/effect/packages/effect/test/ai/AnthropicStructuredOutput.test.ts b/packages/effect/test/ai/AnthropicStructuredOutput.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/AnthropicStructuredOutput.test.ts rename to packages/effect/test/ai/AnthropicStructuredOutput.test.ts diff --git a/repos/effect/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts b/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts rename to packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/Chat.test.ts b/packages/effect/test/ai/Chat.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Chat.test.ts rename to packages/effect/test/ai/Chat.test.ts diff --git a/repos/effect/packages/effect/test/ai/DecisionModel.test.ts b/packages/effect/test/ai/DecisionModel.test.ts similarity index 92% rename from repos/effect/packages/effect/test/ai/DecisionModel.test.ts rename to packages/effect/test/ai/DecisionModel.test.ts index f76e60fe9..cb58a61fc 100644 --- a/repos/effect/packages/effect/test/ai/DecisionModel.test.ts +++ b/packages/effect/test/ai/DecisionModel.test.ts @@ -346,6 +346,70 @@ describe("DecisionModel", () => { ) }) + it.effect("forwards images to an image-capable provider", () => { + const images: ReadonlyArray = [ + { mediaType: "image/png", data: new Uint8Array([137, 80, 78, 71]) }, + { mediaType: "image/jpeg", data: "/9j/4AAQ" }, + { mediaType: "image/webp", data: new URL("https://example.com/image.webp") } + ] + + return Effect.gen(function*() { + const { answers } = yield* DecisionModel.decide(TicketTriage, { input: ticket, images }) + + assert.strictEqual(answers.urgent.probability, 0.9) + }).pipe( + Effect.provide(Layer.effect( + DecisionModel.DecisionModel, + DecisionModel.make({ + supportsImages: true, + decide: (options) => { + assert.deepStrictEqual(options.images, images) + return Effect.succeed({ + answers: triageAnswers, + usage: { inputTokens: undefined, outputTokens: undefined } + }) + } + }) + )) + ) + }) + + for (const supportsImages of [undefined, false]) { + it.effect( + `rejects images with InvalidUserInputError when supportsImages is ${supportsImages}`, + () => + Effect.gen(function*() { + const error = yield* failureOf( + DecisionModel.decide(TicketTriage, { + input: ticket, + images: [{ mediaType: "image/png", data: "iVBORw0KGgo=" }] + }) + ) + + assert.isTrue(AiError.isAiError(error)) + assert.strictEqual(error?.reason._tag, "InvalidUserInputError") + }).pipe(Effect.provide(Layer.effect( + DecisionModel.DecisionModel, + DecisionModel.make({ + supportsImages, + decide: () => Effect.die("A text-only provider must not be called with images") + }) + ))) + ) + } + + for (const images of [undefined, []]) { + it.effect(`allows ${images === undefined ? "omitted" : "empty"} images for a text-only provider`, () => + Effect.gen(function*() { + const { answers } = yield* DecisionModel.decide(TicketTriage, { input: ticket, images }) + + assert.strictEqual(answers.urgent.probability, 0.9) + }).pipe(Effect.provide(makeLayer((options) => { + assert.strictEqual(options.images, undefined) + return Effect.succeed({ answers: triageAnswers, usage: { inputTokens: undefined, outputTokens: undefined } }) + })))) + } + it.effect("a string input is passed to the provider as a string state", () => { const states: Array = [] const Sentiment = Decision.make({ diff --git a/repos/effect/packages/effect/test/ai/EmbeddingModel.test.ts b/packages/effect/test/ai/EmbeddingModel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/EmbeddingModel.test.ts rename to packages/effect/test/ai/EmbeddingModel.test.ts diff --git a/repos/effect/packages/effect/test/ai/LanguageModel.test.ts b/packages/effect/test/ai/LanguageModel.test.ts similarity index 98% rename from repos/effect/packages/effect/test/ai/LanguageModel.test.ts rename to packages/effect/test/ai/LanguageModel.test.ts index 65892245c..11b6f708a 100644 --- a/repos/effect/packages/effect/test/ai/LanguageModel.test.ts +++ b/packages/effect/test/ai/LanguageModel.test.ts @@ -70,6 +70,34 @@ describe("LanguageModel", () => { } describe("generateText", () => { + it.effect("records resolved request options on a client span", () => + LanguageModel.generateText({ prompt: "hi" }).pipe( + TestUtils.withLanguageModel({ + generateText: ({ span }) => { + strictEqual(span.kind, "client") + strictEqual(span.attributes.get("gen_ai.output.type"), "text") + strictEqual(span.attributes.get("effect.ai.tool_choice"), "auto") + strictEqual(span.attributes.get("effect.ai.concurrency"), "unbounded") + return [] + } + }) + )) + + it.effect("JSON-encodes object tool choices", () => + LanguageModel.generateText({ + prompt: "hi", + toolkit: MyToolkit, + toolChoice: { oneOf: ["MyTool"] } + }).pipe( + TestUtils.withLanguageModel({ + generateText: ({ span }) => { + strictEqual(span.attributes.get("effect.ai.tool_choice"), JSON.stringify({ oneOf: ["MyTool"] })) + return [] + } + }), + Effect.provide(MyToolkitLayer) + )) + it.effect("does not resolve tool calls after an incomplete finish", () => Effect.gen(function*() { const calls = yield* Ref.make(0) @@ -1262,6 +1290,19 @@ describe("LanguageModel", () => { }) describe("generateObject", () => { + it.effect("records json output type on the span", () => + LanguageModel.generateObject({ + prompt: "hi", + schema: Schema.Struct({ count: Schema.Number }) + }).pipe( + TestUtils.withLanguageModel({ + generateText: ({ span }) => { + strictEqual(span.attributes.get("gen_ai.output.type"), "json") + return [{ type: "text", text: "{\"count\":1}" }] + } + }) + )) + it.effect("includes full generated text in StructuredOutputError", () => Effect.gen(function*() { const error = yield* LanguageModel.generateObject({ diff --git a/repos/effect/packages/effect/test/ai/LanguageModelRepresentation.test.ts b/packages/effect/test/ai/LanguageModelRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/LanguageModelRepresentation.test.ts rename to packages/effect/test/ai/LanguageModelRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts b/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts rename to packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpSchema.test.ts b/packages/effect/test/ai/McpSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpSchema.test.ts rename to packages/effect/test/ai/McpSchema.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts b/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts b/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts b/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts b/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts b/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts b/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts b/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts b/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts b/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts similarity index 94% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts index 6134c6562..b83472054 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts @@ -198,20 +198,18 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman })) it.effect.skipIf(["2024-11-05", "2025-03-26"].includes(protocol.protocolVersion))( - "MUST require the negotiated protocol-version header after initialization", + "MUST reject a protocol-version header other than the negotiated version after initialization", () => Effect.gen(function*() { const test = yield* McpConformance const initialized = yield* test.initialize() assert.isNotNull(initialized.sessionId) - const missing = yield* test.ping(initialized, { includeProtocolVersion: false }) const mismatched = yield* test.ping(initialized, { id: 3, protocolVersion: protocol.protocolVersion === "2025-11-25" ? "2025-06-18" : "2025-03-26" }) - assert.isAtLeast(missing.status, 400) assert.isAtLeast(mismatched.status, 400) }) ) @@ -333,6 +331,13 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman "Mcp-Protocol-Version": protocol.protocolVersion })) assert.strictEqual(response.status, 400) + const error = yield* test.decodeError(response) + assert.strictEqual(error.id, 2) + assert.strictEqual(error.error.code, McpSchema.INVALID_REQUEST_ERROR_CODE) + assert.strictEqual( + error.error.message, + "MCP-Session-Id header is required; send initialize to start a session" + ) })) it.effect("MUST reject an unknown session identifier with not found", () => Effect.gen(function*() { @@ -363,6 +368,9 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman const initialized = yield* test.initialize() const repeated = yield* test.send(initialized, test.initializeRequest({ id: 2 })) assert.strictEqual(repeated.status, 400) + const error = yield* test.decodeError(repeated) + assert.strictEqual(error.id, 2) + assert.strictEqual(error.error.code, McpSchema.INVALID_REQUEST_ERROR_CODE) const unknown = yield* test.request(jsonRequest("POST", test.initializeRequest({ id: 3 }), { "Mcp-Session-Id": "unknown-session" })) @@ -381,14 +389,17 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman describe.skipIf(["2024-11-05", "2025-03-26"].includes(protocol.protocolVersion))( "Protocol Version Header", () => { - it.effect("MUST apply the revision-specific protocol header requirement", () => + // https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#protocol-version-header + it.effect("SHOULD use the negotiated protocol version when the header is absent", () => Effect.gen(function*() { const test = yield* McpConformance const initialized = yield* test.initialize() const response = yield* test.ping(initialized, { includeProtocolVersion: false }) - assert.strictEqual(response.status, 400) + assert.strictEqual(response.status, 200) + assert.strictEqual(response.headers.get("Mcp-Protocol-Version"), protocol.protocolVersion) + assert.deepStrictEqual((yield* test.decodeResult(response)).result, {}) })) it.effect("MUST accept the negotiated protocol version", () => Effect.gen(function*() { @@ -409,6 +420,13 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman protocolVersion: "2099-01-01" }) assert.strictEqual(response.status, 400) + const error = yield* test.decodeError(response) + assert.strictEqual(error.id, 2) + assert.strictEqual(error.error.code, -32022) + assert.deepStrictEqual(error.error.data, { + supported: [protocol.protocolVersion], + requested: "2099-01-01" + }) })) it.effect("SCENARIO replays the selected protocol version on HTTP responses", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts b/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpProtocol.test.ts b/packages/effect/test/ai/McpServer/McpProtocol.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpProtocol.test.ts rename to packages/effect/test/ai/McpServer/McpProtocol.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts b/packages/effect/test/ai/McpServer/McpServer.test.ts similarity index 93% rename from repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts rename to packages/effect/test/ai/McpServer/McpServer.test.ts index 4f235c711..05dd823f3 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts +++ b/packages/effect/test/ai/McpServer/McpServer.test.ts @@ -2180,6 +2180,178 @@ describe("McpServer", () => { yield* client.ping({}) })) + it.effect("terminates an HTTP session on DELETE without a protocol header when allowSessionTermination is set", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2025_11_25], + allowSessionTermination: true + })) + const headers = yield* initializeHttpSession(harness, McpProtocol.v2025_11_25) + const response = yield* harness.delete({ "Mcp-Session-Id": headers["Mcp-Session-Id"] }) + strictEqual(response.status, 204) + strictEqual(yield* Effect.promise(() => response.text()), "") + strictEqual((yield* harness.post({ jsonrpc: "2.0", id: 3, method: "ping" }, headers)).status, 404) + strictEqual((yield* harness.delete(headers)).status, 404) + })) + + it.effect("rejects DELETE with a missing or unknown session id when termination is enabled", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "SessionDelete", + allowSessionTermination: true + })) + strictEqual((yield* harness.delete()).status, 400) + strictEqual((yield* harness.delete({ "Mcp-Session-Id": "unknown" })).status, 404) + })) + + it.effect("rejects disallowed DELETE Origins without terminating the session", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2025_11_25], + allowSessionTermination: true + })) + const headers = yield* initializeHttpSession(harness, McpProtocol.v2025_11_25) + strictEqual((yield* harness.delete({ ...headers, origin: "https://blocked.example" })).status, 403) + strictEqual((yield* harness.post({ jsonrpc: "2.0", id: 2, method: "ping" }, headers)).status, 200) + strictEqual((yield* harness.delete({ ...headers, origin: "https://allowed.example" })).status, 204) + })) + + it.effect("rejects unsupported or mismatched DELETE protocol versions without terminating the session", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2025_06_18, McpProtocol.v2025_11_25], + allowSessionTermination: true + })) + const headers = yield* initializeHttpSession(harness, McpProtocol.v2025_11_25) + for (const version of ["9999-01-01", "2025-06-18"]) { + strictEqual((yield* harness.delete({ ...headers, "Mcp-Protocol-Version": version })).status, 400) + const ping = yield* harness.post({ jsonrpc: "2.0", id: 2, method: "ping" }, headers) + strictEqual(ping.status, 200) + assert.deepStrictEqual(yield* readMcpHttpResponse(ping), { jsonrpc: "2.0", id: 2, result: {} }) + } + strictEqual((yield* harness.delete(headers)).status, 204) + })) + + it.effect("refuses DELETE for a stateless-only server even when termination is enabled", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "StatelessDelete", + protocols: [McpProtocol.v2026_07_28], + allowSessionTermination: true + })) + const response = yield* harness.delete() + strictEqual(response.status, 405) + strictEqual(response.headers.get("Allow"), "POST") + })) + + it.effect("advertises DELETE on unsupported methods when stateful termination is enabled", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness(makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2026_07_28, McpProtocol.v2025_11_25], + allowSessionTermination: true + })) + for (const method of ["GET", "PUT", "PATCH", "HEAD"] as const) { + const response = yield* Effect.promise(() => harness.handler(new Request("http://localhost/mcp", { method }))) + strictEqual(response.status, 405) + strictEqual(response.headers.get("Allow"), "POST, DELETE") + } + })) + + it.effect("interrupts an in-flight tool on DELETE and returns 404 for its pending POST", () => + Effect.gen(function*() { + const started = yield* Deferred.make() + const interrupted = yield* Deferred.make() + const registration = Layer.effectDiscard(Effect.gen(function*() { + const server = yield* McpServer.McpServer + yield* server.addTool({ + tool: new McpSchema.Tool({ name: "Blocked", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => + Deferred.succeed(started, undefined).pipe( + Effect.andThen(Effect.never), + Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined)) + ) + }) + })) + const harness = yield* makeHttpHarness(registration.pipe(Layer.provideMerge( + makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2025_11_25], + allowSessionTermination: true + }) + ))) + const headers = yield* initializeHttpSession(harness, McpProtocol.v2025_11_25) + const pending = yield* harness.post({ + jsonrpc: "2.0", + id: "blocked-tool", + method: "tools/call", + params: { name: "Blocked", arguments: {} } + }, headers).pipe(Effect.forkScoped) + yield* Deferred.await(started) + strictEqual((yield* harness.delete(headers)).status, 204) + yield* Deferred.await(interrupted) + strictEqual((yield* Fiber.join(pending)).status, 404) + })) + + it.effect("closes an already-started elicitation stream on DELETE", () => + Effect.gen(function*() { + const interrupted = yield* Deferred.make() + const registration = Layer.effectDiscard(Effect.gen(function*() { + const server = yield* McpServer.McpServer + yield* server.addTool({ + tool: new McpSchema.Tool({ name: "Authorize", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => + Effect.gen(function*() { + const client = yield* Effect.serviceOption(McpSchema.McpServerClient).pipe( + Effect.flatMap(Effect.fromOption) + ) + const reverseClient = yield* client.getClient + yield* reverseClient.elicit( + Schema.decodeUnknownSync(McpSchema.Elicit.payloadSchema)({ + mode: "url", + message: "Authorize access", + url: "https://example.com/authorize", + elicitationId: "authorization-1" + }) + ) + return new McpSchema.CallToolResult({ content: [] }) + }).pipe( + Effect.scoped, + Effect.orDie, + Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined)) + ) + }) + })) + const harness = yield* makeHttpHarness(registration.pipe(Layer.provideMerge( + makeServerLayer({ + name: "SessionDelete", + protocols: [McpProtocol.v2025_11_25], + allowSessionTermination: true + }) + ))) + const headers = yield* initializeHttpSession(harness, McpProtocol.v2025_11_25, { elicitation: { url: {} } }) + const response = yield* harness.post({ + jsonrpc: "2.0", + id: "authorize-tool", + method: "tools/call", + params: { name: "Authorize", arguments: {} } + }, headers) + const stream = makeMcpSseReader(response) + yield* Effect.addFinalizer(() => stream.cancel) + const elicitation = yield* stream.take() + strictEqual(elicitation.method, "elicitation/create") + assert.isDefined(elicitation.id) + strictEqual((yield* harness.delete(headers)).status, 204) + yield* Deferred.await(interrupted) + const remaining = yield* stream.drain() + assert.isFalse(remaining.some((message) => message.id === "authorize-tool")) + })) + it.effect("returns an empty 202 for notifications and responses and remains successful for request POSTs", () => Effect.gen(function*() { const { client, httpClient } = yield* makeRouterTestClient(HttpRouter.cors()) @@ -2280,7 +2452,13 @@ describe("McpServer", () => { httpClient.execute ) strictEqual(unsupportedResponse.status, 400) - strictEqual(yield* unsupportedResponse.text, "") + assert.deepInclude(yield* unsupportedResponse.json, { + error: { + code: -32022, + message: "Unsupported protocol version '9999-01-01'", + data: { supported: ["2025-06-18"], requested: "9999-01-01" } + } + }) strictEqual(unsupportedResponse.headers["access-control-allow-origin"], "*") const malformedResponse = yield* HttpClientRequest.post("http://localhost/mcp").pipe( @@ -2291,7 +2469,7 @@ describe("McpServer", () => { httpClient.execute ) strictEqual(malformedResponse.status, 400) - strictEqual(yield* malformedResponse.text, "") + strictEqual(JSON.parse(yield* malformedResponse.text).error.code, -32022) const malformedNoVersionResponse = yield* HttpClientRequest.post("http://localhost/mcp").pipe( HttpClientRequest.setHeader("accept", "application/json, text/event-stream"), @@ -2311,7 +2489,7 @@ describe("McpServer", () => { httpClient.execute ) strictEqual(invalidRequestResponse.status, 400) - strictEqual(yield* invalidRequestResponse.text, "") + strictEqual(JSON.parse(yield* invalidRequestResponse.text).error.code, -32022) const invalidRequestNoVersionResponse = yield* HttpClientRequest.post("http://localhost/mcp").pipe( HttpClientRequest.setHeader("accept", "application/json, text/event-stream"), @@ -2330,7 +2508,7 @@ describe("McpServer", () => { httpClient.execute ) strictEqual(invalidInitializeResponse.status, 400) - strictEqual(yield* invalidInitializeResponse.text, "") + strictEqual(JSON.parse(yield* invalidInitializeResponse.text).error.code, -32022) const responseOnly = yield* HttpClientRequest.post("http://localhost/mcp").pipe( HttpClientRequest.setHeader("accept", "application/json, text/event-stream"), @@ -2339,7 +2517,7 @@ describe("McpServer", () => { httpClient.execute ) strictEqual(responseOnly.status, 400) - strictEqual(yield* responseOnly.text, "") + strictEqual(JSON.parse(yield* responseOnly.text).error.code, -32022) const absentVersionResponse = yield* HttpClientRequest.post("http://localhost/mcp").pipe( HttpClientRequest.setHeader("accept", "application/json, text/event-stream"), diff --git a/repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts b/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts similarity index 99% rename from repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts rename to packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts index 585b19140..1b2b7f382 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts +++ b/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts @@ -1469,6 +1469,21 @@ describe("McpServer protocol adapters", () => { assert.isDefined(currentShared) assert.notProperty(oldShared, "title") assert.strictEqual(currentShared.title, "Shared tool title") + + const response = yield* fixture.post({ jsonrpc: "2.0", id: 2, method: "ping", params: {} }, { + "Mcp-Protocol-Version": "2025-11-25", + "Mcp-Session-Id": currentClient.sessionId + }) + + assert.strictEqual(response.status, 400) + assert.deepStrictEqual(yield* readJsonRpcResponse(response), { + jsonrpc: "2.0", + id: 2, + error: { + code: McpSchema.HEADER_MISMATCH_ERROR_CODE, + message: "MCP-Protocol-Version header '2025-11-25' does not match negotiated protocol version '2025-06-18'" + } + }) })) for (const protocolVersion of ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"] as const) { diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts b/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts similarity index 90% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts index e08c8e092..249377617 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts +++ b/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts @@ -52,19 +52,23 @@ export const makeHttpHarness = Effect.fnUntraced(function*( ) const post = (body: unknown, headers?: HeadersInit) => postText(JSON.stringify(body), headers) + const deleteSession = (headers?: HeadersInit) => + Effect.promise(() => handler(new Request(MCP_ENDPOINT, { method: "DELETE", headers: headers ?? {} }))) return { handler, fetch, post, postText, + delete: deleteSession, responses } as const }) export const initializeHttpSession = Effect.fnUntraced(function*( harness: Effect.Success>, - selectedProtocol: McpProtocol.ProtocolAdapter + selectedProtocol: McpProtocol.ProtocolAdapter, + capabilities: Record = {} ) { const response = yield* harness.post({ jsonrpc: "2.0", @@ -72,7 +76,7 @@ export const initializeHttpSession = Effect.fnUntraced(function*( method: "initialize", params: { protocolVersion: selectedProtocol.protocolVersion, - capabilities: {}, + capabilities, clientInfo: { name: "test", version: "1.0.0" } } }) diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts b/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts b/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts similarity index 88% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts index 8995b0a51..625d38b68 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts +++ b/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts @@ -20,6 +20,7 @@ export const makeServerLayer = (options: { | undefined readonly extensions?: Readonly> | undefined readonly allowedOrigins?: ReadonlyArray | undefined + readonly allowSessionTermination?: boolean | undefined }) => McpServer.layerHttp({ name: options.name, @@ -28,7 +29,8 @@ export const makeServerLayer = (options: { path: "/mcp", protocols: options.protocols ?? [McpProtocol.v2025_06_18], allowedOrigins: ["https://allowed.example"], - extensions: options.extensions + extensions: options.extensions, + allowSessionTermination: options.allowSessionTermination }).pipe( Layer.provideMerge(Layer.succeed( References.CurrentLoggers, diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts b/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2024_11_05.test.ts b/packages/effect/test/ai/McpServer/v2024_11_05.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2024_11_05.test.ts rename to packages/effect/test/ai/McpServer/v2024_11_05.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_03_26.test.ts b/packages/effect/test/ai/McpServer/v2025_03_26.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_03_26.test.ts rename to packages/effect/test/ai/McpServer/v2025_03_26.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_06_18.test.ts b/packages/effect/test/ai/McpServer/v2025_06_18.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_06_18.test.ts rename to packages/effect/test/ai/McpServer/v2025_06_18.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_11_25.test.ts b/packages/effect/test/ai/McpServer/v2025_11_25.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_11_25.test.ts rename to packages/effect/test/ai/McpServer/v2025_11_25.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2026_07_28.test.ts b/packages/effect/test/ai/McpServer/v2026_07_28.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2026_07_28.test.ts rename to packages/effect/test/ai/McpServer/v2026_07_28.test.ts diff --git a/repos/effect/packages/effect/test/ai/Model.test.ts b/packages/effect/test/ai/Model.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Model.test.ts rename to packages/effect/test/ai/Model.test.ts diff --git a/repos/effect/packages/effect/test/ai/OpenAiStructuredOutput.test.ts b/packages/effect/test/ai/OpenAiStructuredOutput.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/OpenAiStructuredOutput.test.ts rename to packages/effect/test/ai/OpenAiStructuredOutput.test.ts diff --git a/repos/effect/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts b/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts rename to packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/Prompt.test.ts b/packages/effect/test/ai/Prompt.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Prompt.test.ts rename to packages/effect/test/ai/Prompt.test.ts diff --git a/repos/effect/packages/effect/test/ai/Response.test.ts b/packages/effect/test/ai/Response.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Response.test.ts rename to packages/effect/test/ai/Response.test.ts diff --git a/repos/effect/packages/effect/test/ai/ResponseIdTracker.test.ts b/packages/effect/test/ai/ResponseIdTracker.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/ResponseIdTracker.test.ts rename to packages/effect/test/ai/ResponseIdTracker.test.ts diff --git a/repos/effect/packages/effect/test/ai/Tokenizer.test.ts b/packages/effect/test/ai/Tokenizer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Tokenizer.test.ts rename to packages/effect/test/ai/Tokenizer.test.ts diff --git a/repos/effect/packages/effect/test/ai/Tool.test.ts b/packages/effect/test/ai/Tool.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Tool.test.ts rename to packages/effect/test/ai/Tool.test.ts diff --git a/repos/effect/packages/effect/test/ai/ToolRepresentation.test.ts b/packages/effect/test/ai/ToolRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/ToolRepresentation.test.ts rename to packages/effect/test/ai/ToolRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/utils.ts b/packages/effect/test/ai/utils.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/utils.ts rename to packages/effect/test/ai/utils.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.array.test.ts b/packages/effect/test/arbitrary/Arbitrary.array.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.array.test.ts rename to packages/effect/test/arbitrary/Arbitrary.array.test.ts diff --git a/packages/effect/test/arbitrary/Arbitrary.causes.test.ts b/packages/effect/test/arbitrary/Arbitrary.causes.test.ts new file mode 100644 index 000000000..d012d5425 --- /dev/null +++ b/packages/effect/test/arbitrary/Arbitrary.causes.test.ts @@ -0,0 +1,80 @@ +import { assert, describe, it } from "@effect/vitest" +import { Cause, Context, Effect, Exit, Option } from "effect" +import * as Arrays from "effect/internal/arbitrary/array" +import * as Model from "effect/internal/arbitrary/model" + +describe("Arbitrary shrink causes", () => { + const wrappers: ReadonlyArray<{ + readonly name: string + readonly wrap: (sample: Model.Sample) => Model.Computation | undefined> + readonly expected: unknown + }> = [ + { + name: "concatenation", + wrap: (sample) => Model.makeSample(sample.value, Model.concatPulls([sample.shrinks!, Cause.done()])), + expected: 1 + }, + { + name: "retained children", + wrap: (sample) => Model.fromRetained(Model.retain(sample)), + expected: 1 + }, + { + name: "filter", + wrap: (sample) => Model.filterSample(sample, () => true)!, + expected: 1 + }, + { + name: "filterMap", + wrap: (sample) => Model.filterMapSample(sample, Option.some), + expected: 1 + }, + { + name: "product", + wrap: (sample) => Model.productSample([sample], (children) => children.map((child) => child.value)), + expected: [1] + }, + { + name: "array", + wrap: (sample) => + Arrays.sample([sample, Model.makeSample(9)], { + fixedCount: 1, + optionalCount: 0, + repeatCount: 1, + tailCount: 0, + minimum: 1 + }), + expected: [1, 9] + } + ] + + for (const { expected, name, wrap } of wrappers) { + it.effect(`does not exhaust ${name} on Done mixed with defects and interruption`, () => + Effect.gen(function*() { + const annotations = Context.make( + Context.Service("test/Arbitrary/ShrinkDiagnostic"), + "shrink diagnostic" + ) + const unexpected = Cause.annotate(Cause.combine(Cause.die("shrink defect"), Cause.interrupt(123)), annotations) + const mixed = Cause.combine(Cause.fail(Cause.Done()), unexpected) + let pulls = 0 + const source = Model.makeSample( + 2, + Effect.suspend(() => { + pulls++ + return pulls === 1 ? Effect.failCause(mixed) : Effect.succeed(Model.makeSample(1)) + }) + ) + const sample = (yield* Model.toEffect(wrap(source)))! + const first = yield* Effect.exit(sample.shrinks!) + + assert.deepStrictEqual(first, Exit.failCause(unexpected)) + assert.strictEqual(pulls, 1) + + const second = yield* sample.shrinks! + assert.strictEqual(second._tag, "Generated") + if (second._tag === "Generated") assert.deepStrictEqual(second.value, expected) + assert.strictEqual(pulls, 2) + })) + } +}) diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.composition.test.ts b/packages/effect/test/arbitrary/Arbitrary.composition.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.composition.test.ts rename to packages/effect/test/arbitrary/Arbitrary.composition.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts b/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts rename to packages/effect/test/arbitrary/Arbitrary.configuration.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts b/packages/effect/test/arbitrary/Arbitrary.test.ts similarity index 97% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts rename to packages/effect/test/arbitrary/Arbitrary.test.ts index 7497ec591..8d609ef71 100644 --- a/repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts +++ b/packages/effect/test/arbitrary/Arbitrary.test.ts @@ -1,5 +1,7 @@ import { assert, describe, it } from "@effect/vitest" import { + Cause, + Context, Deferred, Effect, Exit, @@ -50,6 +52,102 @@ const verifySchemaCatalog = Effect.fnUntraced(function*(entries: ReadonlyArray { + describe("mixed causes", () => { + const annotations = Context.make(Context.Service("test/Arbitrary/Diagnostic"), "original diagnostic") + const causes = [ + ["defects", Cause.die("defect")], + ["interruption", Cause.interrupt(123)], + ["defects and interruption", Cause.combine(Cause.die("defect"), Cause.interrupt(123))] + ] as const + + for (const [name, cause] of causes) { + const unexpected = Cause.annotate(cause, annotations) + + for (const phase of ["initial evaluation", "shrinking"] as const) { + it.effect(`propagates ${name} without typed property failures during ${phase}`, () => + Effect.gen(function*() { + const mixed = Cause.combine(Cause.fail("property failure"), unexpected) + let evaluations = 0 + const exit = yield* Effect.exit(Arbitrary.checkEffect( + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 }))), + () => { + evaluations++ + return phase === "shrinking" && evaluations === 1 ? false : Effect.failCause(mixed) + }, + { runs: 1, seed: 47 } + )) + + assert.deepStrictEqual(exit, Exit.failCause(unexpected)) + assert.strictEqual(evaluations, phase === "shrinking" ? 2 : 1) + })) + } + + it.effect(`propagates ${name} without typed decode failures during generation`, () => + Effect.gen(function*() { + const mixed = Cause.combine( + Cause.fail(new SchemaIssue.Forbidden({ message: "decode failure" })), + unexpected + ) + const schema = Schema.declare((input): input is number => typeof input === "number", { + toCodecArbitrary: () => + Schema.link()( + Schema.Literal(1), + SchemaTransformation.transformEffect({ + decode: () => Effect.failCause(mixed), + encode: () => Effect.succeed(1) + }) + ) + }) + const exit = yield* Effect.exit(Arbitrary.sampleEffect(Arbitrary.schema(schema), { + count: 1, + maxDiscards: 0 + })) + + assert.deepStrictEqual(exit, Exit.failCause(unexpected)) + })) + + it.effect(`propagates ${name} without typed decode failures through composed shrinkers`, () => + Effect.gen(function*() { + const mixed = Cause.combine( + Cause.fail(new SchemaIssue.Forbidden({ message: "shrink decode failure" })), + unexpected + ) + const wrappers: ReadonlyArray< + readonly [string, (arbitrary: Arbitrary.Arbitrary) => Arbitrary.Arbitrary] + > = [ + ["schema", (arbitrary) => arbitrary], + ["filter", Arbitrary.filter(() => true)], + ["filterMap", Arbitrary.filterMap(Result.succeed)], + ["flatMap", Arbitrary.flatMap(Arbitrary.Constant)], + ["all", (arbitrary) => Arbitrary.all([arbitrary])], + ["array", (arbitrary) => Arbitrary.array(arbitrary, { minLength: 1, maxLength: 1 })] + ] + for (const [wrapper, wrap] of wrappers) { + let decodes = 0 + let evaluations = 0 + const schema = Schema.declare((input): input is number => typeof input === "number", { + toCodecArbitrary: () => + Schema.link()( + Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })), + SchemaTransformation.transformEffect({ + decode: (value) => ++decodes === 1 ? Effect.succeed(value) : Effect.failCause(mixed), + encode: Effect.succeed + }) + ) + }) + const exit = yield* Effect.exit(Arbitrary.checkEffect(wrap(Arbitrary.schema(schema)), () => { + evaluations++ + return false + }, { runs: 1, maxDiscards: 0, seed: 47 })) + + assert.deepStrictEqual(exit, Exit.failCause(unexpected), wrapper) + assert.strictEqual(evaluations, 1, wrapper) + assert.strictEqual(decodes, 2, wrapper) + } + })) + } + }) + describe("isArbitrary", () => { it("identifies Arbitrary values", () => { assert.isTrue(Arbitrary.isArbitrary(Arbitrary.schema(Schema.String))) diff --git a/repos/effect/packages/effect/test/cli/Ansi.test.ts b/packages/effect/test/cli/Ansi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Ansi.test.ts rename to packages/effect/test/cli/Ansi.test.ts diff --git a/repos/effect/packages/effect/test/cli/Arguments.test.ts b/packages/effect/test/cli/Arguments.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Arguments.test.ts rename to packages/effect/test/cli/Arguments.test.ts diff --git a/repos/effect/packages/effect/test/cli/Command.test.ts b/packages/effect/test/cli/Command.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Command.test.ts rename to packages/effect/test/cli/Command.test.ts diff --git a/repos/effect/packages/effect/test/cli/Errors.test.ts b/packages/effect/test/cli/Errors.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Errors.test.ts rename to packages/effect/test/cli/Errors.test.ts diff --git a/repos/effect/packages/effect/test/cli/Help.test.ts b/packages/effect/test/cli/Help.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Help.test.ts rename to packages/effect/test/cli/Help.test.ts diff --git a/repos/effect/packages/effect/test/cli/Lexer.test.ts b/packages/effect/test/cli/Lexer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Lexer.test.ts rename to packages/effect/test/cli/Lexer.test.ts diff --git a/repos/effect/packages/effect/test/cli/LogLevel.test.ts b/packages/effect/test/cli/LogLevel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/LogLevel.test.ts rename to packages/effect/test/cli/LogLevel.test.ts diff --git a/repos/effect/packages/effect/test/cli/Param.test.ts b/packages/effect/test/cli/Param.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Param.test.ts rename to packages/effect/test/cli/Param.test.ts diff --git a/repos/effect/packages/effect/test/cli/Primitive.test.ts b/packages/effect/test/cli/Primitive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Primitive.test.ts rename to packages/effect/test/cli/Primitive.test.ts diff --git a/repos/effect/packages/effect/test/cli/Prompt.test.ts b/packages/effect/test/cli/Prompt.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Prompt.test.ts rename to packages/effect/test/cli/Prompt.test.ts diff --git a/repos/effect/packages/effect/test/cli/completions/completions.test.ts b/packages/effect/test/cli/completions/completions.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/completions/completions.test.ts rename to packages/effect/test/cli/completions/completions.test.ts diff --git a/repos/effect/packages/effect/test/cli/completions/descriptor.test.ts b/packages/effect/test/cli/completions/descriptor.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/completions/descriptor.test.ts rename to packages/effect/test/cli/completions/descriptor.test.ts diff --git a/repos/effect/packages/effect/test/cli/fixtures/ComprehensiveCli.ts b/packages/effect/test/cli/fixtures/ComprehensiveCli.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/fixtures/ComprehensiveCli.ts rename to packages/effect/test/cli/fixtures/ComprehensiveCli.ts diff --git a/repos/effect/packages/effect/test/cli/services/MockTerminal.ts b/packages/effect/test/cli/services/MockTerminal.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/services/MockTerminal.ts rename to packages/effect/test/cli/services/MockTerminal.ts diff --git a/repos/effect/packages/effect/test/cli/services/TestActions.ts b/packages/effect/test/cli/services/TestActions.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/services/TestActions.ts rename to packages/effect/test/cli/services/TestActions.ts diff --git a/repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts b/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts rename to packages/effect/test/cluster/ClusterWorkflowEngine.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Entity.test.ts b/packages/effect/test/cluster/Entity.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Entity.test.ts rename to packages/effect/test/cluster/Entity.test.ts diff --git a/repos/effect/packages/effect/test/cluster/EntityProxy.test.ts b/packages/effect/test/cluster/EntityProxy.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/EntityProxy.test.ts rename to packages/effect/test/cluster/EntityProxy.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Envelope.test.ts b/packages/effect/test/cluster/Envelope.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Envelope.test.ts rename to packages/effect/test/cluster/Envelope.test.ts diff --git a/packages/effect/test/cluster/K8sHttpClient.test.ts b/packages/effect/test/cluster/K8sHttpClient.test.ts new file mode 100644 index 000000000..28a071d83 --- /dev/null +++ b/packages/effect/test/cluster/K8sHttpClient.test.ts @@ -0,0 +1,68 @@ +import { assert, describe, it } from "@effect/vitest" +import { Effect, FileSystem, Schema } from "effect" +import * as K8sHttpClient from "effect/cluster/K8sHttpClient" +import { HttpClient, HttpClientResponse } from "effect/http" +import { TestClock } from "effect/testing" + +describe.concurrent("K8sHttpClient", () => { + it.effect("uses the rotated token after the token cache expires", () => { + let token = "token-1" + const authorization: Array = [] + + return Effect.gen(function*() { + const client = yield* K8sHttpClient.K8sHttpClient + const request = client.get("/v1/pods") + yield* request + token = "token-2" + yield* TestClock.adjust("61 seconds") + yield* request + assert.deepStrictEqual(authorization, ["Bearer token-1", "Bearer token-2"]) + }).pipe( + Effect.provide(K8sHttpClient.layer), + Effect.provideService( + FileSystem.FileSystem, + FileSystem.makeNoop({ + readFileString: () => Effect.sync(() => token) + }) + ), + Effect.provideService( + HttpClient.HttpClient, + HttpClient.make((request) => + Effect.sync(() => { + authorization.push(request.headers.authorization) + return HttpClientResponse.fromWeb(request, new Response(null, { status: 200 })) + }) + ) + ) + ) + }) + + describe("Pod", () => { + it.effect("decodes null condition lastTransitionTime values", () => + Effect.gen(function*() { + const pod = yield* Schema.decodeUnknownEffect(K8sHttpClient.Pod)({ + status: { + phase: "Running", + podIP: "10.0.0.1", + hostIP: "10.0.0.2", + conditions: [ + { + type: "Initialized", + status: "True", + lastTransitionTime: null + }, + { + type: "Ready", + status: "False", + lastTransitionTime: null + } + ] + } + }) + + assert.strictEqual(pod.status.conditions[0].lastTransitionTime, null) + assert.strictEqual(pod.isReady, false) + assert.strictEqual(pod.isReadyOrInitializing, true) + })) + }) +}) diff --git a/repos/effect/packages/effect/test/cluster/MessageStorage.test.ts b/packages/effect/test/cluster/MessageStorage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/MessageStorage.test.ts rename to packages/effect/test/cluster/MessageStorage.test.ts diff --git a/repos/effect/packages/effect/test/cluster/ResourceMap.test.ts b/packages/effect/test/cluster/ResourceMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ResourceMap.test.ts rename to packages/effect/test/cluster/ResourceMap.test.ts diff --git a/repos/effect/packages/effect/test/cluster/ResourceRef.test.ts b/packages/effect/test/cluster/ResourceRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ResourceRef.test.ts rename to packages/effect/test/cluster/ResourceRef.test.ts diff --git a/repos/effect/packages/effect/test/cluster/RunnerServer.test.ts b/packages/effect/test/cluster/RunnerServer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/RunnerServer.test.ts rename to packages/effect/test/cluster/RunnerServer.test.ts diff --git a/repos/effect/packages/effect/test/cluster/RunnerStorage.test.ts b/packages/effect/test/cluster/RunnerStorage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/RunnerStorage.test.ts rename to packages/effect/test/cluster/RunnerStorage.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Runners.test.ts b/packages/effect/test/cluster/Runners.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Runners.test.ts rename to packages/effect/test/cluster/Runners.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Sharding.test.ts b/packages/effect/test/cluster/Sharding.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Sharding.test.ts rename to packages/effect/test/cluster/Sharding.test.ts diff --git a/repos/effect/packages/effect/test/cluster/ShardingConfig.test.ts b/packages/effect/test/cluster/ShardingConfig.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ShardingConfig.test.ts rename to packages/effect/test/cluster/ShardingConfig.test.ts diff --git a/repos/effect/packages/effect/test/cluster/TestEntity.ts b/packages/effect/test/cluster/TestEntity.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/TestEntity.ts rename to packages/effect/test/cluster/TestEntity.ts diff --git a/repos/effect/packages/effect/test/cluster/interruptors.test.ts b/packages/effect/test/cluster/interruptors.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/interruptors.test.ts rename to packages/effect/test/cluster/interruptors.test.ts diff --git a/repos/effect/packages/effect/test/devtools/DevToolsClient.test.ts b/packages/effect/test/devtools/DevToolsClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/devtools/DevToolsClient.test.ts rename to packages/effect/test/devtools/DevToolsClient.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Base64.test.ts b/packages/effect/test/encoding/Base64.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Base64.test.ts rename to packages/effect/test/encoding/Base64.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Base64Url.test.ts b/packages/effect/test/encoding/Base64Url.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Base64Url.test.ts rename to packages/effect/test/encoding/Base64Url.test.ts diff --git a/repos/effect/packages/effect/test/encoding/EncodingError.test.ts b/packages/effect/test/encoding/EncodingError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/EncodingError.test.ts rename to packages/effect/test/encoding/EncodingError.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Hex.test.ts b/packages/effect/test/encoding/Hex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Hex.test.ts rename to packages/effect/test/encoding/Hex.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Ini.test.ts b/packages/effect/test/encoding/Ini.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Ini.test.ts rename to packages/effect/test/encoding/Ini.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Ndjson.test.ts b/packages/effect/test/encoding/Ndjson.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Ndjson.test.ts rename to packages/effect/test/encoding/Ndjson.test.ts diff --git a/repos/effect/packages/effect/test/encoding/SchemaBinary.test.ts b/packages/effect/test/encoding/SchemaBinary.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/SchemaBinary.test.ts rename to packages/effect/test/encoding/SchemaBinary.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Sse.test.ts b/packages/effect/test/encoding/Sse.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Sse.test.ts rename to packages/effect/test/encoding/Sse.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Toml.test.ts b/packages/effect/test/encoding/Toml.test.ts similarity index 78% rename from repos/effect/packages/effect/test/encoding/Toml.test.ts rename to packages/effect/test/encoding/Toml.test.ts index 235087e37..5651a8ed7 100644 --- a/repos/effect/packages/effect/test/encoding/Toml.test.ts +++ b/packages/effect/test/encoding/Toml.test.ts @@ -77,6 +77,18 @@ local = 2026-08-05 01:02:03 ) }) + it.each([ + ["LF", "\nvalue", "value"], + ["CRLF", "\r\nvalue", "value"], + ["additional newlines", "\r\n\r\nfirst\r\nsecond", "\r\nfirst\r\nsecond"], + ["leading whitespace", " \r\nvalue", " \r\nvalue"] + ])("trims only an immediate opening newline in multiline strings (%s)", (_, content, expected) => { + assert.deepStrictEqual( + Toml.parse(`basic = """${content}"""\nliteral = '''${content}'''\n`), + { basic: expected, literal: expected } + ) + }) + it("rejects duplicate keys", () => { assert.throws(() => Toml.parse("key = 1\nkey = 2\n")) assert.throws(() => Toml.parse("key = 1__000\n")) diff --git a/repos/effect/packages/effect/test/encoding/Yaml.test.ts b/packages/effect/test/encoding/Yaml.test.ts similarity index 91% rename from repos/effect/packages/effect/test/encoding/Yaml.test.ts rename to packages/effect/test/encoding/Yaml.test.ts index 49d1a238e..93809602e 100644 --- a/repos/effect/packages/effect/test/encoding/Yaml.test.ts +++ b/packages/effect/test/encoding/Yaml.test.ts @@ -89,6 +89,44 @@ indented: >- assert.deepStrictEqual(Yaml.parse(source), { message: expected }) }) + describe.each(["|", ">"])("block scalar chomping (%s)", (style) => { + it.each([ + ["strip", "-", " text\n\n\n", "text"], + ["clip", "", " text\n\n\n", "text\n"], + ["keep", "+", " text\n\n\n", "text\n\n\n"], + ["keep without trailing empty lines", "+", " text\n", "text\n"], + ["empty clip", "", "", ""], + ["empty keep", "+", "", ""], + ["keep with only empty lines", "+", "\n\n", "\n\n"] + ])("applies %s before a sibling key", (_, chomp, content, expected) => { + assert.deepStrictEqual(Yaml.parse(`message: ${style}${chomp}\n${content}next: 1\n`), { + message: expected, + next: 1 + }) + }) + + // YAML 1.2.2 §8.1.1.2: end-of-input is not a final line break. + it.each([ + ["clip without a final line break", "", " text", "text"], + ["keep without a final line break", "+", " text", "text"], + ["keep with trailing empty lines", "+", " text\n\n\n", "text\n\n\n"] + ])("applies %s at end-of-input", (_, chomp, content, expected) => { + assert.deepStrictEqual(Yaml.parse(`message: ${style}${chomp}\n${content}`), { message: expected }) + }) + }) + + it("parses YAML 1.2.2 example 8.6 (empty scalar chomping)", () => { + assert.deepStrictEqual(Yaml.parse("strip: >-\n\nclip: >\n\nkeep: |+\n\n"), { + strip: "", + clip: "", + keep: "\n" + }) + }) + + it("preserves kept trailing lines in block sequence entries", () => { + assert.deepStrictEqual(Yaml.parse("- |+\n first\n\n- second\n"), ["first\n\n", "second"]) + }) + it("resolves aliases", () => { assert.deepStrictEqual( Yaml.parse(` diff --git a/repos/effect/packages/effect/test/eventlog/EventJournal.test.ts b/packages/effect/test/eventlog/EventJournal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventJournal.test.ts rename to packages/effect/test/eventlog/EventJournal.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLog.test.ts b/packages/effect/test/eventlog/EventLog.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLog.test.ts rename to packages/effect/test/eventlog/EventLog.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts b/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts rename to packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogMessage.test.ts b/packages/effect/test/eventlog/EventLogMessage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogMessage.test.ts rename to packages/effect/test/eventlog/EventLogMessage.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogRemote.test.ts b/packages/effect/test/eventlog/EventLogRemote.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogRemote.test.ts rename to packages/effect/test/eventlog/EventLogRemote.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts b/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts rename to packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogSessionAuth.test.ts b/packages/effect/test/eventlog/EventLogSessionAuth.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogSessionAuth.test.ts rename to packages/effect/test/eventlog/EventLogSessionAuth.test.ts diff --git a/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts b/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts new file mode 100644 index 000000000..2f8fe3f1f --- /dev/null +++ b/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts @@ -0,0 +1,339 @@ +import { assert, it } from "@effect/vitest" +import { Cause, Deferred, Effect, Exit, Fiber, Layer, Option, Queue, Stream } from "effect" +import * as EventJournal from "effect/eventlog/EventJournal" +import type { StoreId } from "effect/eventlog/EventLogMessage" +import type * as EventLogServerUnencrypted from "effect/eventlog/EventLogServerUnencrypted" +import * as SqlEventLogServerUnencrypted from "effect/eventlog/SqlEventLogServerUnencrypted" +import { Reactivity } from "effect/reactivity" +import * as SqlClient from "effect/sql/SqlClient" + +let nextNamespace = 0 + +const uniqueNamespace = (prefix: string) => `${prefix}_${++nextNamespace}` + +export const makeOptions = (prefix: string) => { + const namespace = uniqueNamespace(prefix) + return { + entryTablePrefix: `effect_events_${namespace}`, + remoteIdTable: `effect_remote_id_${namespace}`, + insertBatchSize: 2 + } +} + +export const makeStoreId = (prefix: string) => `${uniqueNamespace(prefix)}_store` as StoreId + +export const makeEntry = ( + name: string, + options: { + readonly id?: EventJournal.EntryId | undefined + readonly primaryKey?: string | undefined + } = {} +) => + new EventJournal.Entry({ + id: options.id ?? EventJournal.makeEntryIdUnsafe(), + event: "UserNameSet", + primaryKey: options.primaryKey ?? "user-1", + payload: new TextEncoder().encode(name) + }, { disableChecks: true }) + +export const makeStorage = (options: { + readonly entryTablePrefix?: string + readonly remoteIdTable?: string + readonly insertBatchSize?: number +}) => + SqlEventLogServerUnencrypted.makeStorage(options).pipe( + Effect.orDie + ) + +export const openChanges = (storage: EventLogServerUnencrypted.Storage["Service"], storeId: StoreId) => + storage.changes({ storeId, startSequence: 0, compactors: new Map() }).pipe( + Stream.toQueue({ capacity: "unbounded" }) + ) + +const assertEntries = ( + rows: ReadonlyArray, + expected: ReadonlyArray, + startSequence: number +) => { + assert.deepStrictEqual(rows.map((row) => row.remoteSequence), expected.map((_, index) => startSequence + index)) + assert.deepStrictEqual(rows.map((row) => row.entry.idString), expected.map((entry) => entry.idString)) +} + +export const suite = (name: string, layer: Layer.Layer) => + it.layer( + Layer.mergeAll(Reactivity.layer, layer), + { timeout: "30 seconds" } + )(`SqlEventLogServerUnencrypted (${name})`, (it) => { + it.effect("persists remote id across storage instances", () => + Effect.gen(function*() { + const options = makeOptions("remote_id") + const storageA = yield* makeStorage(options) + const storageB = yield* makeStorage(options) + + const idA = yield* storageA.getId + const idB = yield* storageB.getId + + assert.deepStrictEqual(idA, idB) + })) + + it.effect("replays backlog and then streams live changes without startup duplication", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("changes_backlog_then_live")) + const storeId = makeStoreId("changes") + const entryA = makeEntry("Ada") + const entryB = makeEntry("Grace") + const entryC = makeEntry("Margaret") + + yield* storage.write(storeId, [entryA, entryB]) + + const changes = yield* openChanges(storage, storeId) + const replayed = yield* Queue.takeAll(changes) + + assert.deepStrictEqual(replayed.map((entry) => entry.remoteSequence), [1, 2]) + assert.deepStrictEqual(replayed.map((entry) => entry.entry.idString), [entryA.idString, entryB.idString]) + + yield* storage.write(storeId, [entryC]) + + const next = yield* Queue.take(changes) + assert.strictEqual(next.remoteSequence, 3) + assert.strictEqual(next.entry.idString, entryC.idString) + + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + })) + + it.effect("handles the changes startup race without losing or duplicating rows", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("changes_startup_race")) + + for (let iteration = 0; iteration < 5; iteration++) { + const storeId = makeStoreId(`startup_race_${iteration}`) + const backlogEntry = makeEntry(`Ada_${iteration}`) + const racedEntry = makeEntry(`Grace_${iteration}`) + + yield* storage.write(storeId, [backlogEntry]) + + const changesFiber = yield* openChanges(storage, storeId).pipe(Effect.forkChild) + yield* storage.write(storeId, [racedEntry]) + const changes = yield* Fiber.join(changesFiber) + + const first = yield* Queue.take(changes) + const second = yield* Queue.take(changes) + + assert.deepStrictEqual( + [first.remoteSequence, second.remoteSequence], + [1, 2], + `iteration ${iteration} should deliver exactly the backlog row and the raced row` + ) + assert.deepStrictEqual( + [first.entry.idString, second.entry.idString], + [backlogEntry.idString, racedEntry.idString] + ) + + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + } + })) + + it.effect("isolates reads and streams between stores", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("store_isolation")) + const storeA = makeStoreId("isolation_a") + const storeB = makeStoreId("isolation_b") + const entryA1 = makeEntry("Ada") + const entryB1 = makeEntry("Grace") + const entryA2 = makeEntry("Margaret") + const entryB2 = makeEntry("Linus") + + yield* storage.write(storeA, [entryA1]) + yield* storage.write(storeB, [entryB1]) + + const changesA = yield* openChanges(storage, storeA) + const backlogA = yield* Queue.takeAll(changesA) + assert.deepStrictEqual(backlogA.map((entry) => entry.entry.idString), [entryA1.idString]) + + yield* storage.write(storeB, [entryB2]) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changesA)), true) + + yield* storage.write(storeA, [entryA2]) + const nextA = yield* Queue.take(changesA) + + assert.strictEqual(nextA.remoteSequence, 2) + assert.strictEqual(nextA.entry.idString, entryA2.idString) + })) + + it.effect("commits outer writes but discards a caught nested rollback and its notifications", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("nested_rollback")) + const storeId = makeStoreId("nested_rollback") + const backlog = makeEntry("Ada") + const before = makeEntry("Grace") + const rolledBack = makeEntry("Margaret") + const after = makeEntry("Linus") + const sentinel = makeEntry("Barbara") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assertEntries([yield* Queue.take(changes)], [backlog], 1) + + yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [before]) + const error = yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [rolledBack]) + return yield* Effect.fail("nested rollback") + })).pipe(Effect.flip) + assert.strictEqual(error, "nested rollback") + yield* storage.write(storeId, [after]) + })) + // The sentinel exposes leaked notifications without sleeps. + yield* storage.write(storeId, [sentinel]) + assertEntries(yield* Queue.takeN(changes, 3), [before, after, sentinel], 2) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + + const persisted = yield* Queue.takeAll(yield* openChanges(storage, storeId)) + assertEntries(persisted, [backlog, before, after, sentinel], 1) + })) + + it.effect("persists and streams multiple successful transaction levels in write order exactly once", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("multiple_nesting")) + const storeId = makeStoreId("multiple_nesting") + const backlog = makeEntry("Ada") + const outer = makeEntry("Grace") + const nested = makeEntry("Margaret") + const deepest = makeEntry("Linus") + const after = makeEntry("Barbara") + const sentinel = makeEntry("Donald") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assertEntries([yield* Queue.take(changes)], [backlog], 1) + + yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [outer]) + yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [nested]) + yield* storage.withTransaction(storage.write(storeId, [deepest])) + })) + yield* storage.write(storeId, [after]) + })) + yield* storage.write(storeId, [sentinel]) + assertEntries(yield* Queue.takeN(changes, 5), [outer, nested, deepest, after, sentinel], 2) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + + const persisted = yield* Queue.takeAll(yield* openChanges(storage, storeId)) + assertEntries(persisted, [backlog, outer, nested, deepest, after, sentinel], 1) + })) + + it.effect("streams concurrent sibling transactions in sequence order exactly once", () => + Effect.gen(function*() { + const sql = yield* SqlClient.SqlClient + const completed = yield* Deferred.make() + const release = yield* Deferred.make() + let pauseNext = false + // Pause after savepoint completion releases the SQL permit, before storage merges the buffer. + const delayedSql: SqlClient.SqlClient = Object.assign( + (...args: Parameters) => sql(...args), + sql, + { + withTransaction: (effect: Effect.Effect) => + Effect.suspend(() => { + const pause = pauseNext + pauseNext = false + return sql.withTransaction(effect).pipe( + Effect.tap(() => + pause + ? Deferred.succeed(completed, undefined).pipe(Effect.andThen(Deferred.await(release))) + : Effect.void + ) + ) + }) + } + ) + const storage = yield* makeStorage(makeOptions("sibling_ordering")).pipe( + Effect.provideService(SqlClient.SqlClient, delayedSql) + ) + const storeId = makeStoreId("sibling_ordering") + const backlog = makeEntry("Ada") + const first = makeEntry("Grace") + const second = makeEntry("Margaret") + const sentinel = makeEntry("Linus") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assertEntries([yield* Queue.take(changes)], [backlog], 1) + + yield* storage.withTransaction(Effect.gen(function*() { + pauseNext = true + const writer = yield* storage.write(storeId, [first]).pipe(Effect.forkChild) + yield* Deferred.await(completed) + yield* storage.write(storeId, [second]).pipe( + Effect.ensuring(Deferred.succeed(release, undefined)) + ) + yield* Fiber.join(writer) + })) + yield* storage.write(storeId, [sentinel]) + const rows = yield* Queue.takeN(changes, 3) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + assertEntries(yield* Queue.takeAll(yield* openChanges(storage, storeId)), [backlog, first, second, sentinel], 1) + assertEntries(rows, [first, second, sentinel], 2) + })) + + it.effect("rolls back an interrupted pre-commit body without publishing its writes", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("interrupted_transaction")) + const storeId = makeStoreId("interrupted_transaction") + const backlog = makeEntry("Ada") + const rolledBack = makeEntry("Grace") + const sentinel = makeEntry("Margaret") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assertEntries([yield* Queue.take(changes)], [backlog], 1) + + const written = yield* Deferred.make() + const writer = yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [rolledBack]) + yield* Deferred.succeed(written, undefined) + return yield* Effect.never + })).pipe(Effect.forkChild) + yield* Deferred.await(written) + yield* Fiber.interrupt(writer) + const exit = yield* Fiber.await(writer) + assert.strictEqual(Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause), true) + + yield* storage.write(storeId, [sentinel]) + assertEntries([yield* Queue.take(changes)], [sentinel], 2) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + assertEntries(yield* Queue.takeAll(yield* openChanges(storage, storeId)), [backlog, sentinel], 1) + })) + + it.effect("discards outer rollback rows and notifications, including successful nested writes", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("outer_rollback")) + const storeId = makeStoreId("outer_rollback") + const backlog = makeEntry("Ada") + const rolledBack = makeEntry("Grace") + const nested = makeEntry("Margaret") + const sentinel = makeEntry("Linus") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assertEntries([yield* Queue.take(changes)], [backlog], 1) + + const error = yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [rolledBack]) + yield* storage.withTransaction(storage.write(storeId, [nested])) + return yield* Effect.fail("rollback") + })).pipe(Effect.flip) + assert.strictEqual(error, "rollback") + + yield* storage.write(storeId, [sentinel]) + assertEntries([yield* Queue.take(changes)], [sentinel], 2) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + + const persisted = yield* Queue.takeAll(yield* openChanges(storage, storeId)) + assertEntries(persisted, [backlog, sentinel], 1) + })) + }) diff --git a/repos/effect/packages/effect/test/fixtures/migrator/0001_first.js b/packages/effect/test/fixtures/migrator/0001_first.js similarity index 100% rename from repos/effect/packages/effect/test/fixtures/migrator/0001_first.js rename to packages/effect/test/fixtures/migrator/0001_first.js diff --git a/repos/effect/packages/effect/test/fixtures/text.txt b/packages/effect/test/fixtures/text.txt similarity index 100% rename from repos/effect/packages/effect/test/fixtures/text.txt rename to packages/effect/test/fixtures/text.txt diff --git a/repos/effect/packages/effect/test/http/Cookies.test.ts b/packages/effect/test/http/Cookies.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Cookies.test.ts rename to packages/effect/test/http/Cookies.test.ts diff --git a/repos/effect/packages/effect/test/http/FetchHttpClient.test.ts b/packages/effect/test/http/FetchHttpClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FetchHttpClient.test.ts rename to packages/effect/test/http/FetchHttpClient.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/case-insensitive.test.ts b/packages/effect/test/http/FindMyWay/case-insensitive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/case-insensitive.test.ts rename to packages/effect/test/http/FindMyWay/case-insensitive.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/matching-order.test.ts b/packages/effect/test/http/FindMyWay/matching-order.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/matching-order.test.ts rename to packages/effect/test/http/FindMyWay/matching-order.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/methods.test.ts b/packages/effect/test/http/FindMyWay/methods.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/methods.test.ts rename to packages/effect/test/http/FindMyWay/methods.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/optional-params.test.ts b/packages/effect/test/http/FindMyWay/optional-params.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/optional-params.test.ts rename to packages/effect/test/http/FindMyWay/optional-params.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/params-collisions.test.ts b/packages/effect/test/http/FindMyWay/params-collisions.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/params-collisions.test.ts rename to packages/effect/test/http/FindMyWay/params-collisions.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/path-params-match.test.ts b/packages/effect/test/http/FindMyWay/path-params-match.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/path-params-match.test.ts rename to packages/effect/test/http/FindMyWay/path-params-match.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/querystring.test.ts b/packages/effect/test/http/FindMyWay/querystring.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/querystring.test.ts rename to packages/effect/test/http/FindMyWay/querystring.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/regex.test.ts b/packages/effect/test/http/FindMyWay/regex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/regex.test.ts rename to packages/effect/test/http/FindMyWay/regex.test.ts diff --git a/repos/effect/packages/effect/test/http/Headers.test.ts b/packages/effect/test/http/Headers.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Headers.test.ts rename to packages/effect/test/http/Headers.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpBody.test.ts b/packages/effect/test/http/HttpBody.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpBody.test.ts rename to packages/effect/test/http/HttpBody.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpClient.test.ts b/packages/effect/test/http/HttpClient.test.ts similarity index 96% rename from repos/effect/packages/effect/test/http/HttpClient.test.ts rename to packages/effect/test/http/HttpClient.test.ts index f0f77272e..74662c69b 100644 --- a/repos/effect/packages/effect/test/http/HttpClient.test.ts +++ b/packages/effect/test/http/HttpClient.test.ts @@ -1,10 +1,11 @@ -import { assert, describe, it } from "@effect/vitest" +import { assert, describe, it, vi } from "@effect/vitest" import { strictEqual } from "@effect/vitest/utils" import { Clock, Duration, Effect, Fiber, Layer, Ref, Schema, Stream } from "effect" -import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http" +import { Cookies, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http" import { RateLimiter } from "effect/persistence" import { TestClock } from "effect/testing" import * as Tracer from "effect/Tracer" +import { collectGarbage } from "../utils/gc.ts" const makeStatusClient = Effect.fnUntraced(function*(status: number) { const attempts = yield* Ref.make(0) @@ -38,6 +39,36 @@ const makeRedirectClient = Effect.fnUntraced(function*(status: number, location: const RateLimiterTestLayer = RateLimiter.layer.pipe(Layer.provide(RateLimiter.layerStoreMemory)) describe("HttpClient", () => { + it.skipIf(process.versions.bun !== undefined || process.versions.deno !== undefined)( + "does not retain unread responses without FinalizationRegistry", + async () => { + vi.stubGlobal("FinalizationRegistry", undefined) + try { + // The response registry is selected when HttpClient is imported. + vi.resetModules() + const { Effect } = await import("effect") + const { HttpClient, HttpClientResponse } = await import("effect/http") + let reference: WeakRef | undefined + const client = HttpClient.make((request) => + Effect.sync(() => { + const response = HttpClientResponse.fromWeb(request, new Response("unread")) + reference = new WeakRef(response) + return response + }) + ) + + await Effect.runPromise(Effect.asVoid(client.get("https://example.test/"))) + await Effect.runPromise(collectGarbage) + + assert.isDefined(reference) + assert.isUndefined(reference!.deref()) + } finally { + vi.unstubAllGlobals() + vi.resetModules() + } + } + ) + it.effect("applies JSON revivers to response schema decoders", () => Effect.gen(function*() { const makeResponse = () => @@ -141,7 +172,7 @@ Missing key })) describe("tracer", () => { - it.effect("includes request and response headers by default", () => + it.effect("omits request and response headers by default", () => Effect.gen(function*() { let clientSpan: Tracer.NativeSpan | undefined const tracer = Tracer.make({ @@ -166,8 +197,8 @@ Missing key }).pipe(Effect.provideService(Tracer.Tracer, tracer)) assert(clientSpan !== undefined) - assert.strictEqual(clientSpan.attributes.get("http.request.header.x-request-default"), "request") - assert.strictEqual(clientSpan.attributes.get("http.response.header.x-response-default"), "response") + assert.strictEqual(clientSpan.attributes.get("http.request.header.x-request-default"), undefined) + assert.strictEqual(clientSpan.attributes.get("http.response.header.x-response-default"), undefined) })) it.effect("filters request and response header span attributes", () => @@ -1360,4 +1391,12 @@ Missing key strictEqual(yield* Ref.get(attempts), 3) }).pipe(Effect.provide(RateLimiter.layerStoreMemory))) }) + + it("reads cookies when Headers has no getSetCookie", () => { + // React Native's Headers implementation does not provide getSetCookie. + const source = new Response(null, { headers: { "content-type": "text/plain" } }) + Object.defineProperty(source.headers, "getSetCookie", { value: undefined }) + const response = HttpClientResponse.fromWeb(HttpClientRequest.get("http://localhost"), source) + assert.isTrue(Cookies.isEmpty(response.cookies)) + }) }) diff --git a/repos/effect/packages/effect/test/http/HttpClientRequest.test.ts b/packages/effect/test/http/HttpClientRequest.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpClientRequest.test.ts rename to packages/effect/test/http/HttpClientRequest.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpCompression.test.ts b/packages/effect/test/http/HttpCompression.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpCompression.test.ts rename to packages/effect/test/http/HttpCompression.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpEffect.test.ts b/packages/effect/test/http/HttpEffect.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpEffect.test.ts rename to packages/effect/test/http/HttpEffect.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts b/packages/effect/test/http/HttpMiddleware.test.ts similarity index 85% rename from repos/effect/packages/effect/test/http/HttpMiddleware.test.ts rename to packages/effect/test/http/HttpMiddleware.test.ts index bfe91c667..9d042244b 100644 --- a/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts +++ b/packages/effect/test/http/HttpMiddleware.test.ts @@ -350,11 +350,11 @@ describe("HttpMiddleware", () => { assert.strictEqual(serverSpan.attributes.get("url.path"), "/todos/1") assert.strictEqual(serverSpan.attributes.get("url.query"), "foo=bar") assert.strictEqual(serverSpan.attributes.get("user_agent.original"), "test-agent") - assert.strictEqual(serverSpan.attributes.get("http.request.header.x-request"), "request") - assert.strictEqual(serverSpan.attributes.get("http.request.header.x-request-secret"), "") + assert.strictEqual(serverSpan.attributes.get("http.request.header.x-request"), undefined) + assert.strictEqual(serverSpan.attributes.get("http.request.header.x-request-secret"), undefined) assert.strictEqual(serverSpan.attributes.get("http.response.status_code"), 201) - assert.strictEqual(serverSpan.attributes.get("http.response.header.x-response"), "response") - assert.strictEqual(serverSpan.attributes.get("http.response.header.x-response-secret"), "") + assert.strictEqual(serverSpan.attributes.get("http.response.header.x-response"), undefined) + assert.strictEqual(serverSpan.attributes.get("http.response.header.x-response-secret"), undefined) })) it.effect("skips attributes for unsampled spans", () => @@ -414,7 +414,68 @@ describe("HttpMiddleware", () => { assert.strictEqual(parentInFinalizer, undefined) })) - it.effect("excludes the sent response from a failed stream span", () => + it.effect("fails the span for a rendered 599 without changing the response", () => + Effect.gen(function*() { + let serverSpan: Tracer.Span | undefined + const request = HttpServerRequest.fromWeb(new Request("http://localhost:3000/error")) + const response = HttpServerResponse.empty({ status: 599 }) + const app = Effect.gen(function*() { + serverSpan = yield* Effect.currentSpan + return yield* Effect.fail("handler failed") + }).pipe(Effect.catch(() => Effect.succeed(response))) + + const result = yield* HttpMiddleware.tracer(app).pipe( + Effect.provideService(HttpServerRequest.HttpServerRequest, request), + Effect.provideService(Tracer.Tracer, Tracer.make({ span: (options) => new Tracer.NativeSpan(options) })) + ) + yield* Effect.yieldNow + + assert.strictEqual(result, response) + assert(serverSpan !== undefined && serverSpan.status._tag === "Ended") + assert.deepStrictEqual( + serverSpan.status.exit, + Exit.fail( + new HttpServerError.HttpServerError({ + reason: new HttpServerError.ResponseError({ request, response }) + }) + ) + ) + })) + + it("fails a native span without changing a returned 500 response", async () => { + const stackTraceLimit = Error.stackTraceLimit + try { + Error.stackTraceLimit = 37 + await Effect.runPromise(Effect.gen(function*() { + let serverSpan: Tracer.Span | undefined + const request = HttpServerRequest.fromWeb(new Request("http://localhost:3000/native")) + const response = HttpServerResponse.empty({ status: 500 }) + + const exit = yield* Effect.exit( + HttpMiddleware.tracer(Effect.gen(function*() { + serverSpan = yield* Effect.currentSpan + return response + })).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request)) + ) + assert.deepStrictEqual(exit, Exit.succeed(response)) + assert(serverSpan !== undefined) + assert(serverSpan.status._tag === "Ended") + assert.deepStrictEqual( + serverSpan.status.exit, + Exit.fail( + new HttpServerError.HttpServerError({ + reason: new HttpServerError.ResponseError({ request, response }) + }) + ) + ) + })) + assert.strictEqual(Error.stackTraceLimit, 37) + } finally { + Error.stackTraceLimit = stackTraceLimit + } + }) + + it.effect("preserves the stream failure when the sent response is 500", () => Effect.gen(function*() { let serverSpan: Tracer.NativeSpan | undefined const tracer = Tracer.make({ @@ -425,7 +486,7 @@ describe("HttpMiddleware", () => { }) const request = HttpServerRequest.fromWeb(new Request("http://localhost:3000/stream")) const response = HttpServerResponse.empty({ - status: 200, + status: 500, headers: { "content-type": "text/event-stream" } }) const streamError = new Error("stream failed") @@ -434,7 +495,7 @@ describe("HttpMiddleware", () => { // the stream failure so the server can finish the request correctly. const app = Effect.failCause(Cause.combine(Cause.fail(streamError), Cause.die(response))) - yield* Effect.exit( + const exit = yield* Effect.exit( HttpMiddleware.tracer(app).pipe( Effect.provideService(HttpServerRequest.HttpServerRequest, request), Effect.provideService(Tracer.Tracer, tracer) @@ -442,8 +503,9 @@ describe("HttpMiddleware", () => { ) yield* Effect.yieldNow + assert.deepStrictEqual(exit, Exit.failCause(Cause.combine(Cause.fail(streamError), Cause.die(response)))) assert(serverSpan !== undefined) - assert.strictEqual(serverSpan.attributes.get("http.response.status_code"), 200) + assert.strictEqual(serverSpan.attributes.get("http.response.status_code"), 500) assert.strictEqual(serverSpan.status._tag, "Ended") if (serverSpan.status._tag === "Ended") { assert.deepStrictEqual(serverSpan.status.exit, Exit.fail(streamError)) diff --git a/repos/effect/packages/effect/test/http/HttpPlatform.test.ts b/packages/effect/test/http/HttpPlatform.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpPlatform.test.ts rename to packages/effect/test/http/HttpPlatform.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpRouter.test.ts b/packages/effect/test/http/HttpRouter.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpRouter.test.ts rename to packages/effect/test/http/HttpRouter.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServer.test.ts b/packages/effect/test/http/HttpServer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServer.test.ts rename to packages/effect/test/http/HttpServer.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerError.test.ts b/packages/effect/test/http/HttpServerError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerError.test.ts rename to packages/effect/test/http/HttpServerError.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerRequest.test.ts b/packages/effect/test/http/HttpServerRequest.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerRequest.test.ts rename to packages/effect/test/http/HttpServerRequest.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerRespondable.test.ts b/packages/effect/test/http/HttpServerRespondable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerRespondable.test.ts rename to packages/effect/test/http/HttpServerRespondable.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerResponse.test.ts b/packages/effect/test/http/HttpServerResponse.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerResponse.test.ts rename to packages/effect/test/http/HttpServerResponse.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpStaticServer.test.ts b/packages/effect/test/http/HttpStaticServer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpStaticServer.test.ts rename to packages/effect/test/http/HttpStaticServer.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpStatus.test.ts b/packages/effect/test/http/HttpStatus.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpStatus.test.ts rename to packages/effect/test/http/HttpStatus.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpTraceContext.test.ts b/packages/effect/test/http/HttpTraceContext.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpTraceContext.test.ts rename to packages/effect/test/http/HttpTraceContext.test.ts diff --git a/repos/effect/packages/effect/test/http/Mime.test.ts b/packages/effect/test/http/Mime.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Mime.test.ts rename to packages/effect/test/http/Mime.test.ts diff --git a/repos/effect/packages/effect/test/http/Multipart.test.ts b/packages/effect/test/http/Multipart.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Multipart.test.ts rename to packages/effect/test/http/Multipart.test.ts diff --git a/repos/effect/packages/effect/test/http/SchemaRepresentation.test.ts b/packages/effect/test/http/SchemaRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/SchemaRepresentation.test.ts rename to packages/effect/test/http/SchemaRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/http/Template.test.ts b/packages/effect/test/http/Template.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Template.test.ts rename to packages/effect/test/http/Template.test.ts diff --git a/repos/effect/packages/effect/test/http/Url.test.ts b/packages/effect/test/http/Url.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Url.test.ts rename to packages/effect/test/http/Url.test.ts diff --git a/repos/effect/packages/effect/test/http/UrlParams.test.ts b/packages/effect/test/http/UrlParams.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/UrlParams.test.ts rename to packages/effect/test/http/UrlParams.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/HttpApi.test.ts b/packages/effect/test/httpapi/HttpApi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApi.test.ts rename to packages/effect/test/httpapi/HttpApi.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiBuilder.test.ts b/packages/effect/test/httpapi/HttpApiBuilder.test.ts similarity index 97% rename from repos/effect/packages/effect/test/httpapi/HttpApiBuilder.test.ts rename to packages/effect/test/httpapi/HttpApiBuilder.test.ts index 5c325a6e2..001714ebb 100644 --- a/repos/effect/packages/effect/test/httpapi/HttpApiBuilder.test.ts +++ b/packages/effect/test/httpapi/HttpApiBuilder.test.ts @@ -550,6 +550,38 @@ it.layer(TestServices)("HttpApiBuilder payload content types", (it) => { })) }) +it.layer(TestServices)("HttpApiBuilder array payloads", (it) => { + const Tags = Schema.Struct({ tags: Schema.Array(Schema.String) }) + const Api = HttpApi.make("Api").add( + HttpApiGroup.make("test") + .add(HttpApiEndpoint.post("create", "/create", { + payload: Tags.pipe(HttpApiSchema.asFormUrlEncoded()), + success: Tags + })) + .add(HttpApiEndpoint.get("list", "/list", { payload: Tags.fields, success: Tags })) + ) + const GroupLayer = HttpApiBuilder.group(Api, "test", (handlers) => + handlers + .handle("create", ({ payload }) => Effect.succeed(payload)) + .handle("list", ({ payload }) => Effect.succeed(payload))) + + it.effect("round trips form-urlencoded array payloads with one or more values", () => + Effect.gen(function*() { + const client = yield* HttpApiTest.groups(Api, ["test"]).pipe(Effect.provide(GroupLayer)) + + assert.deepStrictEqual(yield* client.test.create({ payload: { tags: ["a"] } }), { tags: ["a"] }) + assert.deepStrictEqual(yield* client.test.create({ payload: { tags: ["a", "b"] } }), { tags: ["a", "b"] }) + })) + + it.effect("round trips GET array payloads with one or more values", () => + Effect.gen(function*() { + const client = yield* HttpApiTest.groups(Api, ["test"]).pipe(Effect.provide(GroupLayer)) + + assert.deepStrictEqual(yield* client.test.list({ payload: { tags: ["a"] } }), { tags: ["a"] }) + assert.deepStrictEqual(yield* client.test.list({ payload: { tags: ["a", "b"] } }), { tags: ["a", "b"] }) + })) +}) + it.layer(TestServices)("HttpApiBuilder WithHeaders responses", (it) => { it.effect("encodes WithHeaders using the schema for the response status", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiClient.test.ts b/packages/effect/test/httpapi/HttpApiClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApiClient.test.ts rename to packages/effect/test/httpapi/HttpApiClient.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiDocumentation.test.ts b/packages/effect/test/httpapi/HttpApiDocumentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApiDocumentation.test.ts rename to packages/effect/test/httpapi/HttpApiDocumentation.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiEndpoint.test.ts b/packages/effect/test/httpapi/HttpApiEndpoint.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApiEndpoint.test.ts rename to packages/effect/test/httpapi/HttpApiEndpoint.test.ts diff --git a/packages/effect/test/httpapi/HttpApiError.test.ts b/packages/effect/test/httpapi/HttpApiError.test.ts new file mode 100644 index 000000000..1f7bc8801 --- /dev/null +++ b/packages/effect/test/httpapi/HttpApiError.test.ts @@ -0,0 +1,65 @@ +import { assert, describe, it } from "@effect/vitest" +import { Cause, Effect, ErrorReporter, Exit, Schema } from "effect" +import { HttpServerRespondable } from "effect/http" +import { HttpApiError } from "effect/http-api" + +describe("HttpApiError", () => { + for (const kind of ["Params", "Headers", "Query", "Payload"] as const) { + it.effect(`responds with 400 and does not report ${kind} decoding failures`, () => + Effect.gen(function*() { + const schemaError = yield* HttpApiError.HttpApiSchemaError.wrap( + kind, + Schema.decodeUnknownEffect(Schema.Struct({ name: Schema.String }))({ name: 123 }) + ).pipe(Effect.flip) + const reports: Array> = [] + const reporter = ErrorReporter.make(({ cause }) => reports.push(cause)) + + const exit = yield* Effect.fail(schemaError).pipe( + Effect.withErrorReporting, + Effect.provide(ErrorReporter.layer([reporter])), + Effect.exit + ) + + assert.instanceOf(schemaError, HttpApiError.HttpApiSchemaError) + assert.strictEqual(schemaError.kind, kind) + assert.isTrue(Exit.isFailure(exit)) + if (Exit.isFailure(exit)) { + assert.strictEqual(Cause.squash(exit.cause), schemaError) + } + assert.deepStrictEqual(reports, []) + const response = yield* HttpServerRespondable.toResponse(schemaError) + assert.strictEqual(response.status, 400) + })) + } + + for (const kind of ["Body", "ResponseHeaders"] as const) { + const encodingError = HttpApiError.HttpApiSchemaError.wrap( + kind, + Schema.encodeEffect(Schema.Int)(1.5) + ).pipe(Effect.flip) + + it.effect(`responds with 500 for ${kind} encoding failures`, () => + Effect.gen(function*() { + const error = yield* encodingError + const response = yield* HttpServerRespondable.toResponse(error) + assert.strictEqual(response.status, 500) + })) + + it.effect(`reports ${kind} encoding failures`, () => + Effect.gen(function*() { + const error = yield* encodingError + const reports: Array> = [] + const reporter = ErrorReporter.make(({ cause }) => reports.push(cause)) + + const exit = yield* Effect.fail(error).pipe( + Effect.withErrorReporting, + Effect.provide(ErrorReporter.layer([reporter])), + Effect.exit + ) + + assert.isTrue(Exit.isFailure(exit)) + assert.strictEqual(reports.length, 1) + assert.strictEqual(Cause.squash(reports[0]), error) + })) + } +}) diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiSchema.test.ts b/packages/effect/test/httpapi/HttpApiSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApiSchema.test.ts rename to packages/effect/test/httpapi/HttpApiSchema.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/HttpApiSecurity.test.ts b/packages/effect/test/httpapi/HttpApiSecurity.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/HttpApiSecurity.test.ts rename to packages/effect/test/httpapi/HttpApiSecurity.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/OpenApi.test.ts b/packages/effect/test/httpapi/OpenApi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/OpenApi.test.ts rename to packages/effect/test/httpapi/OpenApi.test.ts diff --git a/repos/effect/packages/effect/test/httpapi/OpenApiRepresentation.test.ts b/packages/effect/test/httpapi/OpenApiRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/httpapi/OpenApiRepresentation.test.ts rename to packages/effect/test/httpapi/OpenApiRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/internal/Uuid.test.ts b/packages/effect/test/internal/Uuid.test.ts similarity index 100% rename from repos/effect/packages/effect/test/internal/Uuid.test.ts rename to packages/effect/test/internal/Uuid.test.ts diff --git a/repos/effect/packages/effect/test/net/IpInterface.test.ts b/packages/effect/test/net/IpInterface.test.ts similarity index 100% rename from repos/effect/packages/effect/test/net/IpInterface.test.ts rename to packages/effect/test/net/IpInterface.test.ts diff --git a/repos/effect/packages/effect/test/net/IpNetwork.test.ts b/packages/effect/test/net/IpNetwork.test.ts similarity index 100% rename from repos/effect/packages/effect/test/net/IpNetwork.test.ts rename to packages/effect/test/net/IpNetwork.test.ts diff --git a/repos/effect/packages/effect/test/net/NetAddress.test.ts b/packages/effect/test/net/NetAddress.test.ts similarity index 100% rename from repos/effect/packages/effect/test/net/NetAddress.test.ts rename to packages/effect/test/net/NetAddress.test.ts diff --git a/repos/effect/packages/effect/test/observability/OtlpConfig.test.ts b/packages/effect/test/observability/OtlpConfig.test.ts similarity index 93% rename from repos/effect/packages/effect/test/observability/OtlpConfig.test.ts rename to packages/effect/test/observability/OtlpConfig.test.ts index 5c65f100b..1e84c8e77 100644 --- a/repos/effect/packages/effect/test/observability/OtlpConfig.test.ts +++ b/packages/effect/test/observability/OtlpConfig.test.ts @@ -21,19 +21,22 @@ describe("Otlp.layerFromConfig", () => { assert.isDefined(logRequest) assert.strictEqual(logRequest!.headers["x-signal"], "logs") assert.strictEqual(logRequest!.headers["x-shared"], undefined) + assert.strictEqual(logRequest!.headers.authorization, undefined) assert.isDefined(metricRequest) assert.strictEqual(metricRequest!.headers["x-shared"], "shared") + assert.strictEqual(metricRequest!.headers.authorization, "Basic dXNlcjpwYXNzd29yZA==") const metric = findMetric(metricRequest!.body as OtlpMetricsBody, "otel_config_counter") assert.strictEqual(metric?.sum?.aggregationTemporality, 1) assert.isDefined(traceRequest) assert.strictEqual(traceRequest!.headers["x-shared"], "shared") + assert.strictEqual(traceRequest!.headers.authorization, "Basic dXNlcjpwYXNzd29yZA==") }).pipe( Effect.provide(testLayer({ OTEL_SERVICE_NAME: "otel-config-test", OTEL_EXPORTER_OTLP_ENDPOINT: "http://collector.example", - OTEL_EXPORTER_OTLP_HEADERS: "x-shared=shared", + OTEL_EXPORTER_OTLP_HEADERS: "x-shared=shared,authorization=Basic%20dXNlcjpwYXNzd29yZA==", OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: "http://logs.example/custom", OTEL_EXPORTER_OTLP_LOGS_HEADERS: "x-signal=logs", OTEL_LOGS_EXPORTER: "console, OTLP", diff --git a/repos/effect/packages/effect/test/observability/OtlpEnvHeaders.test.ts b/packages/effect/test/observability/OtlpEnvHeaders.test.ts similarity index 100% rename from repos/effect/packages/effect/test/observability/OtlpEnvHeaders.test.ts rename to packages/effect/test/observability/OtlpEnvHeaders.test.ts diff --git a/repos/effect/packages/effect/test/observability/OtlpExporter.test.ts b/packages/effect/test/observability/OtlpExporter.test.ts similarity index 95% rename from repos/effect/packages/effect/test/observability/OtlpExporter.test.ts rename to packages/effect/test/observability/OtlpExporter.test.ts index 76d973c7c..7ddb780bf 100644 --- a/repos/effect/packages/effect/test/observability/OtlpExporter.test.ts +++ b/packages/effect/test/observability/OtlpExporter.test.ts @@ -1,9 +1,9 @@ import { assert, describe, it } from "@effect/vitest" import { Clock, ConfigProvider, Deferred, type Duration, Effect, Exit, Fiber, Layer, Metric, Ref, Scope } from "effect" -import { HttpBody, HttpClient, HttpClientResponse } from "effect/http" -import type { HttpClientError } from "effect/http" +import { type Headers, HttpBody, HttpClient, type HttpClientError, HttpClientResponse } from "effect/http" import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/observability" import { TestClock } from "effect/testing" +import * as Version from "effect/Version" const makeHttpClient = Effect.fnUntraced(function*(retryAfter: string | undefined) { const attempts = yield* Ref.make(0) @@ -35,12 +35,13 @@ const makeExporter = ( readonly maxBatchSize?: number | "disabled" readonly shutdownTimeout?: Duration.Input readonly body?: (data: Array) => HttpBody.HttpBody + readonly headers?: Headers.Input | undefined } ) => OtlpExporter.make({ label: "OtlpExporterTest", url: "http://localhost:4318/v1/logs", - headers: undefined, + headers: options?.headers, exportInterval: options?.exportInterval ?? "1 hour", maxBatchSize: options?.maxBatchSize ?? 1, body: (data) => [options?.body?.(data) ?? HttpBody.empty, Effect.void], @@ -107,6 +108,26 @@ const makeControlledHttpClient = Effect.fnUntraced(function*(requestCount: numbe }) describe("OtlpExporter", () => { + it.effect("appends the versioned exporter User-Agent to a user-supplied value", () => + Effect.gen(function*() { + const userAgents: Array = [] + const httpClient = HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + userAgents.push(request.headers["user-agent"]) + return HttpClientResponse.fromWeb(request, new Response()) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + yield* Effect.scoped(Effect.gen(function*() { + const exporter = yield* makeExporter(httpClient, { headers: { "UsEr-AgEnT": "my-app/1.0" }, maxBatchSize: 10 }) + exporter.push({ value: 1 }) + })) + assert.deepStrictEqual(userAgents, [ + `my-app/1.0 OTel-OTLP-Exporter-JavaScript-Effect-OtlpExporterTest/${Version.getCurrentVersion()}` + ]) + })) + it.effect("allows an in-flight timer export to finish during shutdown", () => Effect.gen(function*() { const scope = yield* Scope.make() diff --git a/packages/effect/test/observability/OtlpLogger.test.ts b/packages/effect/test/observability/OtlpLogger.test.ts new file mode 100644 index 000000000..0e24f5719 --- /dev/null +++ b/packages/effect/test/observability/OtlpLogger.test.ts @@ -0,0 +1,114 @@ +import { assert, describe, it } from "@effect/vitest" +import { Cause, Clock, Effect, Layer } from "effect" +import { HttpClient, HttpClientResponse } from "effect/http" +import type { HttpClientError, HttpClientRequest } from "effect/http" +import { OtlpExporter, OtlpLogger, OtlpSerialization } from "effect/observability" +import { TestClock } from "effect/testing" +import * as Version from "effect/Version" + +const capture = () => { + const requests: Array = [] + const httpClient = HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + requests.push(request) + return HttpClientResponse.fromWeb(request, new Response()) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + return { requests, httpClient } as const +} + +const bodyOf = (request: HttpClientRequest.HttpClientRequest) => { + assert(request.body._tag === "Uint8Array") + return JSON.parse(new TextDecoder().decode(request.body.body)) +} + +const makeTestLayer = (httpClient: HttpClient.HttpClient) => + OtlpLogger.layer({ + url: "http://localhost:4318/v1/logs", + resource: { serviceName: "test", serviceVersion: "service-version" }, + exportInterval: "1 hour", + mergeWithExisting: false + }).pipe( + Layer.provide(OtlpSerialization.layerJson), + Layer.provide(Layer.succeed(HttpClient.HttpClient, httpClient)) + ) + +const logRecordOf = (request: HttpClientRequest.HttpClientRequest) => + bodyOf(request).resourceLogs[0].scopeLogs[0].logRecords[0] + +const attributesOf = (request: HttpClientRequest.HttpClientRequest) => + Object.fromEntries( + logRecordOf(request).attributes.map( + (attribute: { key: string; value: Record }) => [attribute.key, attribute.value] + ) + ) + +describe("OtlpLogger", () => { + it.effect("keeps event time separate from observed time", () => { + const { httpClient, requests } = capture() + return Effect.scoped(Effect.gen(function*() { + const logger = yield* OtlpLogger.make({ + url: "http://localhost:4318/v1/logs", + resource: { serviceName: "test" }, + exportInterval: "1 hour" + }) + const fiber = yield* Effect.fiber + const observedTime = yield* Clock.currentTimeNanos + logger.log({ + fiber, + message: "test", + logLevel: "Info", + cause: Cause.empty, + date: new Date(1234) + }) + yield* (yield* OtlpExporter.Flusher).flush + + const record = logRecordOf(requests[0]) + assert.strictEqual(record.timeUnixNano, "1234000000") + assert.strictEqual(record.observedTimeUnixNano, observedTime.toString()) + })).pipe( + Effect.provide(OtlpExporter.layerFlusher), + Effect.provide(OtlpSerialization.layerJson), + Effect.provideService(HttpClient.HttpClient, httpClient) + ) + }) + + it.effect("namespaces generated attributes and lets them override annotations", () => { + const { httpClient, requests } = capture() + return Effect.gen(function*() { + const fiber = yield* Effect.fiber + yield* Effect.gen(function*() { + yield* TestClock.adjust("5 millis") + yield* Effect.logError( + "boom", + Cause.fail(new TypeError("cause message", { cause: new Error("nested failure") })) + ) + }).pipe( + Effect.withLogSpan("op"), + Effect.annotateLogs({ + "effect.fiberId": -1, + "effect.log_span.op": -1, + "exception.type": "annotated type", + "exception.message": "annotated message", + "exception.stacktrace": "annotated stack" + }) + ) + yield* (yield* OtlpExporter.Flusher).flush + + const keys = logRecordOf(requests[0]).attributes.map((attribute: { key: string }) => attribute.key) + assert.strictEqual(new Set(keys).size, keys.length) + const attributes = attributesOf(requests[0]) + assert.deepStrictEqual(attributes["effect.fiberId"], { intValue: fiber.id }) + assert.deepStrictEqual(attributes["effect.log_span.op"], { intValue: 5 }) + assert.deepStrictEqual(attributes["exception.type"], { stringValue: "TypeError" }) + assert.deepStrictEqual(attributes["exception.message"], { stringValue: "cause message" }) + assert.include(attributes["exception.stacktrace"].stringValue, "nested failure") + assert.deepStrictEqual(bodyOf(requests[0]).resourceLogs[0].scopeLogs[0].scope, { + name: "effect", + version: Version.getCurrentVersion() + }) + }).pipe(Effect.provide(makeTestLayer(httpClient))) + }) +}) diff --git a/repos/effect/packages/effect/test/observability/OtlpMetrics.test.ts b/packages/effect/test/observability/OtlpMetrics.test.ts similarity index 79% rename from repos/effect/packages/effect/test/observability/OtlpMetrics.test.ts rename to packages/effect/test/observability/OtlpMetrics.test.ts index a18dcb0c8..6fd6f429b 100644 --- a/repos/effect/packages/effect/test/observability/OtlpMetrics.test.ts +++ b/packages/effect/test/observability/OtlpMetrics.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Array, Context, Deferred, Effect, Fiber, Layer, Metric, Predicate, Ref } from "effect" +import { Array, Context, Deferred, Duration, Effect, Fiber, Layer, Metric, Predicate, Ref } from "effect" import { HttpClient, type HttpClientError, HttpClientResponse } from "effect/http" import { OtlpExporter, OtlpMetrics, OtlpSerialization } from "effect/observability" import { TestClock } from "effect/testing" @@ -39,14 +39,6 @@ describe("OtlpMetrics", () => { yield* Metric.update(frequency, "a") yield* Metric.update(frequency, "a") yield* Metric.update(frequency, "b") - const summary = Metric.summary("repro_summary", { - maxAge: "1 minute", - maxSize: 100, - quantiles: [0.5] - }) - yield* Metric.update(summary, 10) - yield* Metric.update(summary, 20) - yield* Metric.update(summary, 30) const flusher = yield* OtlpExporter.Flusher yield* flusher.flush yield* TestClock.adjust("60 seconds") @@ -63,10 +55,6 @@ describe("OtlpMetrics", () => { assert.strictEqual(findFrequencyValue(second, "repro_frequency", "a"), 2) assert.strictEqual(findFrequencyValue(first, "repro_frequency", "b"), 1) assert.strictEqual(findFrequencyValue(second, "repro_frequency", "b"), 1) - assert.strictEqual(findMetric(first, "repro_summary_count")?.sum?.dataPoints[0].asInt, 3) - assert.strictEqual(findMetric(second, "repro_summary_count")?.sum?.dataPoints[0].asInt, 3) - assert.strictEqual(findMetric(first, "repro_summary_sum")?.sum?.dataPoints[0].asDouble, 60) - assert.strictEqual(findMetric(second, "repro_summary_sum")?.sum?.dataPoints[0].asDouble, 60) assert.strictEqual( findMetric(second, "repro_counter")?.sum?.dataPoints[0].startTimeUnixNano, findMetric(first, "repro_counter")?.sum?.dataPoints[0].startTimeUnixNano @@ -249,44 +237,6 @@ describe("OtlpMetrics", () => { assert.strictEqual(secondA?.asInt, 3) // Cumulative: 2+1=3 assert.strictEqual(secondB?.asInt, 3) // Cumulative: 1+2=3 }).pipe(Effect.provide(TestLayerCumulative))) - - it.effect("reports summary count and sum across export intervals", () => - Effect.gen(function*() { - const metricName = "cumulative_summary_test" - const summary = Metric.summary(metricName, { - description: "Test summary", - maxAge: "1 minute", - maxSize: 100, - quantiles: [0.5, 0.9, 0.99] - }) - - // First interval: observe 10, 20, 30 (count=3, sum=60) - yield* Metric.update(summary, 10) - yield* Metric.update(summary, 20) - yield* Metric.update(summary, 30) - yield* triggerExport - - // Second interval: observe 40 (cumulative count=4, sum=100) - yield* Metric.update(summary, 40) - yield* triggerExport - - const requests = yield* MockHttpClient.requests - assert.isAtLeast(requests.length, 2) - - // First export - check count and sum metrics - const firstCount = findMetric(requests[0], `${metricName}_count`) - assert.strictEqual(firstCount?.sum?.dataPoints[0].asInt, 3) - - const firstSum = findMetric(requests[0], `${metricName}_sum`) - assert.strictEqual(firstSum?.sum?.dataPoints[0].asDouble, 60) - - // Second export should report cumulative values - const secondCount = findMetric(requests[1], `${metricName}_count`) - assert.strictEqual(secondCount?.sum?.dataPoints[0].asInt, 4) - - const secondSum = findMetric(requests[1], `${metricName}_sum`) - assert.strictEqual(secondSum?.sum?.dataPoints[0].asDouble, 100) - }).pipe(Effect.provide(TestLayerCumulative))) }) describe("delta temporality", () => { @@ -405,93 +355,62 @@ describe("OtlpMetrics", () => { assert.strictEqual(secondA?.asInt, 1) // Delta: 3-2=1 assert.strictEqual(secondB?.asInt, 2) // Delta: 3-1=2 }).pipe(Effect.provide(TestLayerDelta))) + }) - it.effect("reports summary count and sum deltas across export intervals", () => + describe("Summary", () => { + it.effect("exports a cumulative Summary with window quantiles", () => Effect.gen(function*() { - const metricName = "delta_summary_test" - const summary = Metric.summary(metricName, { - description: "Test summary", - maxAge: "1 minute", - maxSize: 100, - quantiles: [0.5, 0.9, 0.99] - }) - - // First interval: observe 10, 20, 30 (count=3, sum=60) + const summary = Metric.summary("summary_test", { maxAge: "15 seconds", maxSize: 100, quantiles: [0.5] }) yield* Metric.update(summary, 10) yield* Metric.update(summary, 20) yield* Metric.update(summary, 30) yield* triggerExport - - // Second interval: observe 40 (delta count=1, delta sum=40) yield* Metric.update(summary, 40) yield* triggerExport - - const requests = yield* MockHttpClient.requests - assert.isAtLeast(requests.length, 2) - - // First export - const firstCount = findMetric(requests[0], `${metricName}_count`) - assert.strictEqual(firstCount?.sum?.dataPoints[0].asInt, 3) - - const firstSum = findMetric(requests[0], `${metricName}_sum`) - assert.strictEqual(firstSum?.sum?.dataPoints[0].asDouble, 60) - - // Second export should report delta values - const secondCount = findMetric(requests[1], `${metricName}_count`) - assert.strictEqual(secondCount?.sum?.dataPoints[0].asInt, 1) // Delta: 4-3=1 - - const secondSum = findMetric(requests[1], `${metricName}_sum`) - assert.strictEqual(secondSum?.sum?.dataPoints[0].asDouble, 40) // Delta: 100-60=40 - }).pipe(Effect.provide(TestLayerDelta))) - }) - - describe("Summary", () => { - it.effect("exports quantile metrics", () => - Effect.gen(function*() { - const metricName = "summary_quantiles_test" - const summary = Metric.summary(metricName, { - description: "Test summary", - maxAge: "1 minute", - maxSize: 100, - quantiles: [0.5, 0.9] - }) - - yield* Metric.update(summary, 10) - yield* Metric.update(summary, 20) - yield* Metric.update(summary, 30) + // Every observation is now older than maxAge yield* triggerExport const requests = yield* MockHttpClient.requests - assert.isAtLeast(requests.length, 1) + const metrics = requests[0].resourceMetrics.flatMap((r) => r.scopeMetrics.flatMap((s) => s.metrics)) + assert.deepStrictEqual(metrics.map((metric) => metric.name), ["summary_test"]) + const points = requests.map((request) => findMetric(request, "summary_test")?.summary?.dataPoints[0]) + assert.deepStrictEqual( + points.map((point) => ({ count: point?.count, sum: point?.sum, quantileValues: point?.quantileValues })), + [ + { count: 3, sum: 60, quantileValues: [{ quantile: 0.5, value: 20 }] }, + { count: 4, sum: 100, quantileValues: [{ quantile: 0.5, value: 40 }] }, + { count: 4, sum: 100, quantileValues: [] } + ] + ) + assert.strictEqual(points[2]?.startTimeUnixNano, points[0]?.startTimeUnixNano) + }).pipe(Effect.provide(TestLayerDelta), Effect.provideService(Metric.MetricRegistry, new Map()))) + }) - // Check that quantiles metric exists - const quantilesMetric = findMetric(requests[0], `${metricName}_quantiles`) - assert.isDefined(quantilesMetric) - assert.isDefined(quantilesMetric?.sum) + it.effect("maps unit attributes to UCUM", () => + Effect.gen(function*() { + yield* Metric.update(Metric.timer("unit_timer_test", { boundaries: [10, 100] }), Duration.millis(5)) + yield* Metric.update(Metric.gauge("unit_custom_test", { attributes: { unit: "{request}" } }), 1) + yield* triggerExport - // Should have data points for min, max, and each quantile (0.5, 0.9) - const dataPoints = quantilesMetric?.sum?.dataPoints ?? [] - assert.isAtLeast(dataPoints.length, 4) // min, 0.5, 0.9, max + const [request] = yield* MockHttpClient.requests + assert.strictEqual(findMetric(request, "unit_timer_test")?.unit, "ms") + assert.strictEqual(findMetric(request, "unit_custom_test")?.unit, "{request}") + }).pipe(Effect.provide(TestLayerCumulative), Effect.provideService(Metric.MetricRegistry, new Map()))) - // Find min and max quantile data points - const minPoint = dataPoints.find((dp) => - dp.attributes.some((attr) => - attr.key === "quantile" && - Predicate.hasProperty(attr.value, "stringValue") && - attr.value.stringValue === "min" - ) - ) - const maxPoint = dataPoints.find((dp) => - dp.attributes.some((attr) => - attr.key === "quantile" && - Predicate.hasProperty(attr.value, "stringValue") && - attr.value.stringValue === "max" - ) - ) - assert.strictEqual(minPoint?.asDouble, 10) - assert.strictEqual(maxPoint?.asDouble, 30) - }).pipe(Effect.provide(TestLayerCumulative))) - }) + it.effect("keeps bigint precision beyond 2^53", () => + Effect.gen(function*() { + const counter = Metric.counter("bigint_counter_test", { bigint: true }) + yield* Metric.update(counter, 9007199254740993n) + yield* Metric.update(Metric.gauge("bigint_gauge_test", { bigint: true }), -9007199254740993n) + yield* triggerExport + yield* Metric.update(counter, 9007199254740993n) + yield* triggerExport + + const [first, second] = yield* MockHttpClient.requests + assert.strictEqual(findMetric(first, "bigint_counter_test")?.sum?.dataPoints[0].asInt, "9007199254740993") + assert.strictEqual(findMetric(second, "bigint_counter_test")?.sum?.dataPoints[0].asInt, "9007199254740993") + assert.strictEqual(findMetric(first, "bigint_gauge_test")?.gauge?.dataPoints[0].asInt, "-9007199254740993") + }).pipe(Effect.provide(TestLayerDelta), Effect.provideService(Metric.MetricRegistry, new Map()))) describe("Gauge (no temporality)", () => { it.effect.each([ @@ -554,6 +473,16 @@ interface OtlpMetric { readonly dataPoints: Array readonly aggregationTemporality: number } | undefined + readonly summary?: { + readonly dataPoints: Array + } | undefined +} + +interface OtlpSummaryDataPoint { + readonly startTimeUnixNano?: string | undefined + readonly count?: number | undefined + readonly sum?: number | undefined + readonly quantileValues?: Array<{ quantile: number; value: number }> | undefined } interface OtlpNumberDataPoint { @@ -561,7 +490,7 @@ interface OtlpNumberDataPoint { readonly startTimeUnixNano?: string | undefined readonly timeUnixNano?: string | undefined readonly asDouble?: number | undefined - readonly asInt?: number | undefined + readonly asInt?: number | string | undefined } interface OtlpHistogramDataPoint { @@ -644,7 +573,7 @@ const findMetric = (request: OtlpExportRequest, name: string): OtlpMetric | unde return undefined } -const findFrequencyValue = (request: OtlpExportRequest, name: string, key: string): number | undefined => +const findFrequencyValue = (request: OtlpExportRequest, name: string, key: string): number | string | undefined => findMetric(request, name)?.sum?.dataPoints.find((dataPoint) => dataPoint.attributes.some((attribute) => attribute.key === "key" && diff --git a/repos/effect/packages/effect/test/observability/OtlpResource.test.ts b/packages/effect/test/observability/OtlpResource.test.ts similarity index 74% rename from repos/effect/packages/effect/test/observability/OtlpResource.test.ts rename to packages/effect/test/observability/OtlpResource.test.ts index b2c652dd5..a42ebd7f3 100644 --- a/repos/effect/packages/effect/test/observability/OtlpResource.test.ts +++ b/packages/effect/test/observability/OtlpResource.test.ts @@ -1,10 +1,17 @@ import { assert, describe, it } from "@effect/vitest" -import { ConfigProvider, Effect } from "effect" +import { Cause, ConfigProvider, Effect, Exit } from "effect" import { OtlpResource } from "effect/observability" +import * as Version from "effect/Version" const attributesRecord = (resource: OtlpResource.Resource): Record => Object.fromEntries(resource.attributes.map((attribute) => [attribute.key, attribute.value.stringValue])) +const sdk = { + "telemetry.sdk.name": "effect", + "telemetry.sdk.language": "nodejs", + "telemetry.sdk.version": Version.getCurrentVersion() +} + describe("OtlpResource", () => { describe("fromConfig", () => { it.effect("decodes percent-encoded OTEL_RESOURCE_ATTRIBUTES", () => @@ -41,6 +48,7 @@ describe("OtlpResource", () => { }) assert.deepStrictEqual(attributesRecord(resource), { + ...sdk, "custom.attribute": "explicit", "service.name": "explicit-service", "service.version": "explicit-version" @@ -69,6 +77,7 @@ describe("OtlpResource", () => { }) assert.deepStrictEqual(attributesRecord(resource), { + ...sdk, "custom.attribute": "explicit", "service.name": "explicit-attribute-service", "service.version": "explicit-attribute-version" @@ -92,6 +101,7 @@ describe("OtlpResource", () => { const resource = yield* OtlpResource.fromConfig() assert.deepStrictEqual(attributesRecord(resource), { + ...sdk, "service.name": "env-service", "service.version": "env-version" }) @@ -115,6 +125,7 @@ describe("OtlpResource", () => { }) assert.deepStrictEqual(attributesRecord(resource), { + ...sdk, "service.name": "explicit-service" }) }).pipe( @@ -125,6 +136,36 @@ describe("OtlpResource", () => { }) ) )) + + it.effect("dies when no service name is configured", () => + Effect.gen(function*() { + const exit = yield* Effect.exit(OtlpResource.fromConfig()) + assert(Exit.isFailure(exit) && Cause.hasDies(exit.cause)) + assert.include(Cause.pretty(exit.cause), "OTEL_SERVICE_NAME") + }).pipe( + Effect.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromEnv({ env: {} })) + )) + }) + + it("lets custom attributes override SDK defaults and service options override attributes", () => { + const resource = OtlpResource.make({ + serviceName: "test", + serviceVersion: "1.0.0", + attributes: { + "telemetry.sdk.name": "custom", + "telemetry.sdk.language": "webjs", + "telemetry.sdk.version": "custom-version", + "service.name": "attribute-service", + "service.version": "attribute-version" + } + }) + assert.deepStrictEqual(resource.attributes, [ + { key: "telemetry.sdk.name", value: { stringValue: "custom" } }, + { key: "telemetry.sdk.language", value: { stringValue: "webjs" } }, + { key: "telemetry.sdk.version", value: { stringValue: "custom-version" } }, + { key: "service.name", value: { stringValue: "test" } }, + { key: "service.version", value: { stringValue: "1.0.0" } } + ]) }) describe("unknownToAttributeValue", () => { diff --git a/packages/effect/test/observability/OtlpSerialization.test.ts b/packages/effect/test/observability/OtlpSerialization.test.ts new file mode 100644 index 000000000..42a37fe56 --- /dev/null +++ b/packages/effect/test/observability/OtlpSerialization.test.ts @@ -0,0 +1,59 @@ +import { assert, describe, it } from "@effect/vitest" +import { Effect } from "effect" +import { OtlpSerialization } from "effect/observability" +import { encodeAnyValue } from "effect/observability/internal/otlpProtobuf" + +describe("OtlpSerialization", () => { + it("encodes a negative protobuf int64 as a ten-byte varint", () => { + const encoded = encodeAnyValue({ intValue: -1 }) + assert.strictEqual(encoded.length, 11) + assert.deepStrictEqual(Array.from(encoded), [0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]) + }) + + it.effect("layerProtobuf encodes Summary metrics", () => + Effect.gen(function*() { + const serialization = yield* OtlpSerialization.OtlpSerialization + const body = serialization.metrics({ + resourceMetrics: [{ + resource: { attributes: [], droppedAttributesCount: 0 }, + scopeMetrics: [{ + scope: { name: "s" }, + metrics: [{ + name: "s", + summary: { + dataPoints: [{ + attributes: [], + startTimeUnixNano: "1", + timeUnixNano: "2", + count: 3, + sum: 6, + quantileValues: [{ quantile: 0.5, value: 2 }] + }] + } + }] + }] + }] + }) + assert.strictEqual(body._tag, "Uint8Array") + assert.deepStrictEqual( + Array.from(body._tag === "Uint8Array" ? body.body : []), + [ + [0x0a, 0x4a], // resource_metrics + [0x0a, 0x00], // resource + [0x12, 0x46], // scope_metrics + [0x0a, 0x03, 0x0a, 0x01, 0x73], // scope { name: "s" } + [0x12, 0x3f], // metrics + [0x0a, 0x01, 0x73], // name: "s" + [0x5a, 0x3a], // summary (field 11) + [0x0a, 0x38], // data_points + [0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // start_time_unix_nano: 1 + [0x19, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // time_unix_nano: 2 + [0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // count: 3 + [0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40], // sum: 6.0 + [0x32, 0x12], // quantile_values + [0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f], // quantile: 0.5 + [0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40] // value: 2.0 + ].flat() + ) + }).pipe(Effect.provide(OtlpSerialization.layerProtobuf))) +}) diff --git a/packages/effect/test/observability/OtlpTracer.test.ts b/packages/effect/test/observability/OtlpTracer.test.ts new file mode 100644 index 000000000..e9cecd6ea --- /dev/null +++ b/packages/effect/test/observability/OtlpTracer.test.ts @@ -0,0 +1,35 @@ +import { assert, describe, it } from "@effect/vitest" +import { Effect, Layer } from "effect" +import { HttpClient, HttpClientResponse } from "effect/http" +import type { HttpClientError } from "effect/http" +import { OtlpExporter, OtlpSerialization, OtlpTracer } from "effect/observability" +import * as Version from "effect/Version" + +describe("OtlpTracer", () => { + it.effect("uses effect as the instrumentation scope, not the service name", () => { + const scopes: Array<{ name: string; version: string }> = [] + const httpClient = HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + assert(request.body._tag === "Uint8Array") + const body = JSON.parse(new TextDecoder().decode(request.body.body)) + scopes.push(body.resourceSpans[0].scopeSpans[0].scope) + return HttpClientResponse.fromWeb(request, new Response()) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + const layer = OtlpTracer.layer({ + url: "http://localhost:4318/v1/traces", + resource: { serviceName: "test", serviceVersion: "service-version" }, + exportInterval: "1 hour" + }).pipe( + Layer.provide(OtlpSerialization.layerJson), + Layer.provide(Layer.succeed(HttpClient.HttpClient, httpClient)) + ) + return Effect.gen(function*() { + yield* Effect.void.pipe(Effect.withSpan("test")) + yield* (yield* OtlpExporter.Flusher).flush + assert.deepStrictEqual(scopes, [{ name: "effect", version: Version.getCurrentVersion() }]) + }).pipe(Effect.provide(layer)) + }) +}) diff --git a/repos/effect/packages/effect/test/observability/PrometheusMetrics.test.ts b/packages/effect/test/observability/PrometheusMetrics.test.ts similarity index 100% rename from repos/effect/packages/effect/test/observability/PrometheusMetrics.test.ts rename to packages/effect/test/observability/PrometheusMetrics.test.ts diff --git a/repos/effect/packages/effect/test/persistence/KeyValueStore.test.ts b/packages/effect/test/persistence/KeyValueStore.test.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/KeyValueStore.test.ts rename to packages/effect/test/persistence/KeyValueStore.test.ts diff --git a/repos/effect/packages/effect/test/persistence/KeyValueStoreTest.ts b/packages/effect/test/persistence/KeyValueStoreTest.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/KeyValueStoreTest.ts rename to packages/effect/test/persistence/KeyValueStoreTest.ts diff --git a/repos/effect/packages/effect/test/persistence/PersistedCache.test.ts b/packages/effect/test/persistence/PersistedCache.test.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/PersistedCache.test.ts rename to packages/effect/test/persistence/PersistedCache.test.ts diff --git a/repos/effect/packages/effect/test/persistence/PersistedCacheTest.ts b/packages/effect/test/persistence/PersistedCacheTest.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/PersistedCacheTest.ts rename to packages/effect/test/persistence/PersistedCacheTest.ts diff --git a/repos/effect/packages/effect/test/persistence/PersistedQueue.test.ts b/packages/effect/test/persistence/PersistedQueue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/PersistedQueue.test.ts rename to packages/effect/test/persistence/PersistedQueue.test.ts diff --git a/repos/effect/packages/effect/test/persistence/PersistedQueueTest.ts b/packages/effect/test/persistence/PersistedQueueTest.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/PersistedQueueTest.ts rename to packages/effect/test/persistence/PersistedQueueTest.ts diff --git a/repos/effect/packages/effect/test/persistence/RateLimiter.test.ts b/packages/effect/test/persistence/RateLimiter.test.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/RateLimiter.test.ts rename to packages/effect/test/persistence/RateLimiter.test.ts diff --git a/repos/effect/packages/effect/test/persistence/RateLimiterTest.ts b/packages/effect/test/persistence/RateLimiterTest.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/RateLimiterTest.ts rename to packages/effect/test/persistence/RateLimiterTest.ts diff --git a/repos/effect/packages/effect/test/persistence/Redis.test.ts b/packages/effect/test/persistence/Redis.test.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/Redis.test.ts rename to packages/effect/test/persistence/Redis.test.ts diff --git a/repos/effect/packages/effect/test/persistence/SqlCleanupTest.ts b/packages/effect/test/persistence/SqlCleanupTest.ts similarity index 100% rename from repos/effect/packages/effect/test/persistence/SqlCleanupTest.ts rename to packages/effect/test/persistence/SqlCleanupTest.ts diff --git a/repos/effect/packages/effect/test/process/ChildProcess.test.ts b/packages/effect/test/process/ChildProcess.test.ts similarity index 100% rename from repos/effect/packages/effect/test/process/ChildProcess.test.ts rename to packages/effect/test/process/ChildProcess.test.ts diff --git a/repos/effect/packages/effect/test/process/ChildProcessSpawnerTest.ts b/packages/effect/test/process/ChildProcessSpawnerTest.ts similarity index 100% rename from repos/effect/packages/effect/test/process/ChildProcessSpawnerTest.ts rename to packages/effect/test/process/ChildProcessSpawnerTest.ts diff --git a/repos/effect/packages/effect/test/process/fixtures/bash/no-permissions.sh b/packages/effect/test/process/fixtures/bash/no-permissions.sh similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/bash/no-permissions.sh rename to packages/effect/test/process/fixtures/bash/no-permissions.sh diff --git a/repos/effect/packages/effect/test/process/fixtures/bash/parent-exits-early.sh b/packages/effect/test/process/fixtures/bash/parent-exits-early.sh similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/bash/parent-exits-early.sh rename to packages/effect/test/process/fixtures/bash/parent-exits-early.sh diff --git a/repos/effect/packages/effect/test/process/fixtures/bash/spawn-children.sh b/packages/effect/test/process/fixtures/bash/spawn-children.sh similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/bash/spawn-children.sh rename to packages/effect/test/process/fixtures/bash/spawn-children.sh diff --git a/repos/effect/packages/effect/test/process/fixtures/config/SHOUTING b/packages/effect/test/process/fixtures/config/SHOUTING similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/config/SHOUTING rename to packages/effect/test/process/fixtures/config/SHOUTING diff --git a/repos/effect/packages/effect/test/process/fixtures/config/integer b/packages/effect/test/process/fixtures/config/integer similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/config/integer rename to packages/effect/test/process/fixtures/config/integer diff --git a/repos/effect/packages/effect/test/process/fixtures/config/nested/config b/packages/effect/test/process/fixtures/config/nested/config similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/config/nested/config rename to packages/effect/test/process/fixtures/config/nested/config diff --git a/repos/effect/packages/effect/test/process/fixtures/config/secret b/packages/effect/test/process/fixtures/config/secret similarity index 100% rename from repos/effect/packages/effect/test/process/fixtures/config/secret rename to packages/effect/test/process/fixtures/config/secret diff --git a/repos/effect/packages/effect/test/reactivity/AsyncResult.test.ts b/packages/effect/test/reactivity/AsyncResult.test.ts similarity index 100% rename from repos/effect/packages/effect/test/reactivity/AsyncResult.test.ts rename to packages/effect/test/reactivity/AsyncResult.test.ts diff --git a/repos/effect/packages/effect/test/reactivity/Atom.test.ts b/packages/effect/test/reactivity/Atom.test.ts similarity index 98% rename from repos/effect/packages/effect/test/reactivity/Atom.test.ts rename to packages/effect/test/reactivity/Atom.test.ts index cd281885d..0c77d2c89 100644 --- a/repos/effect/packages/effect/test/reactivity/Atom.test.ts +++ b/packages/effect/test/reactivity/Atom.test.ts @@ -192,6 +192,53 @@ describe("Atom", { concurrent: false }, () => { } }) + it("windowFocusSignal works when window is undefined", () => { + const r = AtomRegistry.make() + try { + expect(r.get(Atom.windowFocusSignal)).toEqual(0) + } finally { + r.dispose() + } + }) + + it("refreshOnWindowFocus works when window is undefined", () => { + const r = AtomRegistry.make() + try { + expect(r.get(Atom.refreshOnWindowFocus(Atom.make(1)))).toEqual(1) + } finally { + r.dispose() + } + }) + + it("searchParam keeps the hash and omits an empty query", async () => { + const r = AtomRegistry.make() + const location = new URL("http://localhost/path?q=1#section") + const urls: Array = [] + vitest.stubGlobal("window", { + location, + history: { + pushState: (_state: unknown, _title: string, url: string) => { + urls.push(url) + location.href = new URL(url, location).href + } + }, + addEventListener: () => {}, + removeEventListener: () => {} + }) + + try { + const q = Atom.searchParam("q") + r.set(q, "2") + await vitest.advanceTimersByTimeAsync(500) + r.set(q, "") + await vitest.advanceTimersByTimeAsync(500) + expect(urls).toEqual(["/path?q=2#section", "/path#section"]) + } finally { + r.dispose() + vitest.unstubAllGlobals() + } + }) + it("runtime", async () => { const count = counterRuntime.atom(Counter.use((_) => _.get)).pipe( Atom.withLabel("count") diff --git a/repos/effect/packages/effect/test/reactivity/AtomHttpApi.test.ts b/packages/effect/test/reactivity/AtomHttpApi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/reactivity/AtomHttpApi.test.ts rename to packages/effect/test/reactivity/AtomHttpApi.test.ts diff --git a/repos/effect/packages/effect/test/reactivity/AtomRpc.test.ts b/packages/effect/test/reactivity/AtomRpc.test.ts similarity index 100% rename from repos/effect/packages/effect/test/reactivity/AtomRpc.test.ts rename to packages/effect/test/reactivity/AtomRpc.test.ts diff --git a/repos/effect/packages/effect/test/rpc/Rpc.test.ts b/packages/effect/test/rpc/Rpc.test.ts similarity index 100% rename from repos/effect/packages/effect/test/rpc/Rpc.test.ts rename to packages/effect/test/rpc/Rpc.test.ts diff --git a/repos/effect/packages/effect/test/rpc/RpcClient.test.ts b/packages/effect/test/rpc/RpcClient.test.ts similarity index 76% rename from repos/effect/packages/effect/test/rpc/RpcClient.test.ts rename to packages/effect/test/rpc/RpcClient.test.ts index 1ca9e1fa1..88c13947d 100644 --- a/repos/effect/packages/effect/test/rpc/RpcClient.test.ts +++ b/packages/effect/test/rpc/RpcClient.test.ts @@ -1,8 +1,8 @@ import { assert, describe, it } from "@effect/vitest" -import { Cause, Deferred, Effect, Exit, Fiber, Layer, Queue, Schedule, Schema, Stream } from "effect" +import { Cause, Deferred, Effect, Exit, Fiber, Layer, Queue, Schedule, Schema, Stream, Tracer } from "effect" import * as HttpClient from "effect/http/HttpClient" import * as HttpClientResponse from "effect/http/HttpClientResponse" -import { Rpc, RpcClient, RpcGroup, RpcMessage, RpcSchema, RpcSerialization } from "effect/rpc" +import { Rpc, RpcClient, RpcGroup, RpcMessage, RpcSchema, RpcSerialization, RpcServer } from "effect/rpc" import { RpcClientDefect, RpcClientError } from "effect/rpc/RpcClientError" import * as Socket from "effect/socket/Socket" import { TestClock } from "effect/testing" @@ -425,6 +425,91 @@ describe("RpcClient", () => { assert.strictEqual(error.reason._tag, "SocketReadError") })) + it.effect("keeps in-flight streams alive on non-pong frames without any pongs", () => + Effect.gen(function*() { + const requestSent = yield* Deferred.make() + const frames = yield* Queue.unbounded() + const events = yield* Queue.unbounded() + const write = () => Effect.asVoid(Deferred.succeed(requestSent, void 0)) + const socket = Socket.make({ + reader: Effect.succeed({ + pull: Queue.take(frames).pipe(Effect.map((frame) => [frame] as const)), + upgrade: Socket.SocketUpgradeError.unsupported + }), + writer: Effect.succeed({ write, writeAll: write }) + }) + const protocol = yield* RpcClient.makeProtocolSocket().pipe( + Effect.provideService(Socket.Socket, socket), + Effect.provide(RpcSerialization.layerNdjson) + ) + const client = yield* RpcClient.make(TestGroup, { + generateRequestId: () => RpcMessage.RequestId("0") + }).pipe(Effect.provideService(RpcClient.Protocol, protocol)) + const streamFiber = yield* client.Events().pipe( + Stream.runForEach((event) => Queue.offer(events, event)), + Effect.forkChild + ) + + yield* Deferred.await(requestSent) + for (const event of ["first", "second", "third"]) { + yield* TestClock.adjust("4 seconds") + assert.isUndefined(streamFiber.pollUnsafe()) + yield* Queue.offer(frames, JSON.stringify({ _tag: "Chunk", requestId: "0", values: [event] }) + "\n") + assert.strictEqual(yield* Queue.take(events), event) + } + // Cross another ping tick after the third frame, still without a pong. + yield* TestClock.adjust("4 seconds") + assert.isUndefined(streamFiber.pollUnsafe()) + })) + + it.effect("allows a delayed pong within a custom ping timeout but fails after silence", () => + Effect.gen(function*() { + const requestSent = yield* Deferred.make() + const frames = yield* Queue.unbounded() + const frameRead = yield* Deferred.make() + const write = () => Effect.asVoid(Deferred.succeed(requestSent, void 0)) + const socket = Socket.make({ + reader: Effect.succeed({ + pull: Queue.take(frames).pipe( + Effect.tap(() => Deferred.succeed(frameRead, void 0)), + Effect.map((frame) => [frame] as const) + ), + upgrade: Socket.SocketUpgradeError.unsupported + }), + writer: Effect.succeed({ write, writeAll: write }) + }) + const context = yield* Layer.build( + RpcClient.layerProtocolSocket({ + pingInterval: "2 seconds", + pingTimeout: "15 seconds", + retryPolicy: Schedule.spaced("1 hour") + }).pipe( + Layer.provide(RpcSerialization.layerNdjson), + Layer.provide(Layer.succeed(Socket.Socket, socket)) + ) + ) + const client = yield* RpcClient.make(TestGroup).pipe(Effect.provide(context)) + const streamFiber = yield* client.Events().pipe(Stream.runDrain, Effect.exit, Effect.forkChild) + + yield* Deferred.await(requestSent) + // The old fixed timeout fails at 10s, before this delayed pong arrives. + yield* TestClock.adjust("12 seconds") + assert.isUndefined(streamFiber.pollUnsafe()) + yield* Queue.offer(frames, JSON.stringify({ _tag: "Pong" }) + "\n") + yield* Deferred.await(frameRead) + yield* TestClock.adjust("14 seconds") + assert.isUndefined(streamFiber.pollUnsafe()) + + // At 28s the next 2s tick is 16s after the last frame, beyond the 15s timeout. + yield* TestClock.adjust("2 seconds") + assert.isDefined(streamFiber.pollUnsafe()) + const exit = yield* Fiber.join(streamFiber) + assert(Exit.isFailure(exit)) + const error = Cause.squash(exit.cause) + assert.instanceOf(error, RpcClientError) + assert.strictEqual(error.reason._tag, "SocketReadError") + })) + it.effect("fails in-flight streams when transient retries are exhausted", () => Effect.gen(function*() { const requestSent = yield* Deferred.make() @@ -516,4 +601,72 @@ describe("RpcClient", () => { assert.instanceOf(error, RpcClientError) assert.strictEqual(error.reason._tag, "SocketOpenError") })) + + describe("tracing", () => { + const SpanGroup = TestGroup.prefix("Echo.") + + const call = ( + method: "Ping" | "Events", + options: { spanPrefix?: string; spanAttributes?: Record } = {} + ) => { + const spans: Array = [] + return Effect.gen(function*() { + // oxlint-disable-next-line prefer-const + let client!: Effect.Success< + ReturnType, never>> + > + const server = yield* RpcServer.makeNoSerialization(SpanGroup, { + ...options, + onFromServer: (response) => client.write(response) + }) + client = yield* RpcClient.makeNoSerialization(SpanGroup, { + ...options, + supportsAck: true, + onFromClient: ({ message }) => server.write(0, message) + }) + yield* method === "Ping" ? client.client["Echo.Ping"]() : Stream.runDrain(client.client["Echo.Events"]()) + return spans + }).pipe( + Effect.provide(SpanGroup.toLayer({ + "Echo.Ping": () => Effect.succeed("pong"), + "Echo.Events": () => Stream.make("event") + })), + Effect.provideService( + Tracer.Tracer, + Tracer.make({ + span(options) { + const span = new Tracer.NativeSpan(options) + spans.push(span) + return span + } + }) + ) + ) + } + + it.effect("records RPC span defaults for streaming calls", () => + Effect.gen(function*() { + const spans = yield* call("Events") + assert.deepStrictEqual(spans.map((span) => span.kind).sort(), ["client", "server"]) + for (const span of spans) { + assert.strictEqual(span.name, "Echo.Events") + assert.strictEqual(span.attributes.get("rpc.system.name"), "effect_rpc") + assert.strictEqual(span.attributes.get("rpc.method"), "Echo.Events") + } + })) + + it.effect("uses spanPrefix and spanAttributes for unary calls", () => + Effect.gen(function*() { + const spans = yield* call("Ping", { + spanPrefix: "Custom", + spanAttributes: { "rpc.system.name": "custom_rpc", "rpc.method": "custom_method" } + }) + assert.deepStrictEqual(spans.map((span) => span.kind).sort(), ["client", "server"]) + for (const span of spans) { + assert.strictEqual(span.name, "Custom.Echo.Ping") + assert.strictEqual(span.attributes.get("rpc.system.name"), "custom_rpc") + assert.strictEqual(span.attributes.get("rpc.method"), "custom_method") + } + })) + }) }) diff --git a/repos/effect/packages/effect/test/rpc/RpcSerialization.test.ts b/packages/effect/test/rpc/RpcSerialization.test.ts similarity index 100% rename from repos/effect/packages/effect/test/rpc/RpcSerialization.test.ts rename to packages/effect/test/rpc/RpcSerialization.test.ts diff --git a/repos/effect/packages/effect/test/rpc/RpcServer.test.ts b/packages/effect/test/rpc/RpcServer.test.ts similarity index 94% rename from repos/effect/packages/effect/test/rpc/RpcServer.test.ts rename to packages/effect/test/rpc/RpcServer.test.ts index 0322986ec..a39c094fa 100644 --- a/repos/effect/packages/effect/test/rpc/RpcServer.test.ts +++ b/packages/effect/test/rpc/RpcServer.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Deferred, Effect, Exit, Fiber, Layer, Queue, Ref, Schema, Scope, Sink, Stdio, Stream } from "effect" -import { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http" +import { Headers, HttpEffect, HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http" import * as NetAddress from "effect/net/NetAddress" import { Rpc, RpcGroup, RpcSerialization, RpcServer } from "effect/rpc" import * as RpcMessage from "effect/rpc/RpcMessage" @@ -48,6 +48,33 @@ const producedWithoutReadingFramedBody = Effect.fnUntraced(function*( }) describe("RpcServer", () => { + for ( + const [name, serialization] of [ + ["ndjson", RpcSerialization.layerNdjson], + ["ndJsonRpc", RpcSerialization.layerNdJsonRpc()], + ["schemaBinary", RpcSerialization.layerSchemaBinary()] + ] as const + ) { + it.effect(`returns HTTP 200 with an empty body for an empty ${name} POST`, () => + Effect.gen(function*() { + const group = RpcGroup.make(Rpc.make("ping", { success: Schema.String })) + const httpEffect = yield* RpcServer.toHttpEffect(group).pipe( + Effect.provide(Layer.mergeAll( + group.toLayer({ ping: () => Effect.succeed("pong") }), + serialization + )) + ) + const handler = HttpEffect.toWebHandler(httpEffect) + const response = yield* Effect.promise(() => + handler(new Request("http://test/rpc", { method: "POST", body: "" })) + ) + const body = yield* Effect.promise(() => response.text()) + + assert.strictEqual(response.status, 200) + assert.strictEqual(body, "") + })) + } + it.effect("should drain the response when stdin ends during request startup", () => Effect.gen(function*() { const started = yield* Deferred.make() diff --git a/repos/effect/packages/effect/test/schema/CodePoints.test.ts b/packages/effect/test/schema/CodePoints.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/CodePoints.test.ts rename to packages/effect/test/schema/CodePoints.test.ts diff --git a/repos/effect/packages/effect/test/schema/Graph.test.ts b/packages/effect/test/schema/Graph.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/Graph.test.ts rename to packages/effect/test/schema/Graph.test.ts diff --git a/repos/effect/packages/effect/test/schema/HMR.test.ts b/packages/effect/test/schema/HMR.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/HMR.test.ts rename to packages/effect/test/schema/HMR.test.ts diff --git a/repos/effect/packages/effect/test/schema/Schema.test.ts b/packages/effect/test/schema/Schema.test.ts similarity index 99% rename from repos/effect/packages/effect/test/schema/Schema.test.ts rename to packages/effect/test/schema/Schema.test.ts index a78cec290..9d35320b9 100644 --- a/repos/effect/packages/effect/test/schema/Schema.test.ts +++ b/packages/effect/test/schema/Schema.test.ts @@ -4708,6 +4708,36 @@ Expected a value between -2147483648 and 2147483647` strictEqual(secondCalls, 1) })) + it(`mode: "oneOf" succeeds on each execution of the same suspended decode effect`, () => { + const first = Schema.String.pipe(Schema.decode({ + decode: SchemaGetter.transformEffect((s) => Effect.sync(() => s)), + encode: SchemaGetter.passthrough() + })) + const schema = Schema.Union([first, Schema.String.check(Schema.isMinLength(5))], { mode: "oneOf" }) + const effect = SchemaParser.decodeUnknownEffect(schema)("a") + + strictEqual(Effect.runSync(effect), "a") + strictEqual(Effect.runSync(effect), "a") + }) + + it(`mode: "anyOf" does not reuse a previous success when all members now fail`, () => { + let succeeds = true + const first = Schema.String.pipe(Schema.decode({ + decode: SchemaGetter.transformEffect((s) => + Effect.suspend(() => + succeeds ? Effect.succeed(s) : Effect.fail(new SchemaIssue.Forbidden({ message: "first failed" })) + ) + ), + encode: SchemaGetter.passthrough() + })) + const schema = Schema.Union([first, Schema.String.check(Schema.isMinLength(5))]) + const effect = SchemaParser.decodeUnknownEffect(schema)("a") + + strictEqual(Effect.runSync(effect), "a") + succeeds = false + strictEqual(Effect.runSync(Effect.flip(effect))._tag, "AnyOf") + }) + it.effect(`mode: "oneOf" detects asynchronous successes in member order`, () => Effect.gen(function*() { const firstStarted = yield* Deferred.make() @@ -9533,11 +9563,23 @@ pointed message Schema.Struct({ _tag: Schema.tag("B"), type: Schema.tag("TypeB"), b: Schema.FiniteFromString }) ]).pipe(Schema.toTaggedUnion("type")) + strictEqual(schema.tag, "type") + // cases deepStrictEqual(schema.cases.TypeA, schema.members[0]) deepStrictEqual(schema.cases.TypeB, schema.members[1]) }) + it("should expose a symbol tag", () => { + const tag = Symbol.for("tag") + const schema = Schema.Union([ + Schema.Struct({ [tag]: Schema.tag("A") }), + Schema.Struct({ [tag]: Schema.tag("B") }) + ]).pipe(Schema.toTaggedUnion(tag)) + + strictEqual(schema.tag, tag) + }) + it("should throw on duplicate discriminants", () => { throws( () => @@ -9602,6 +9644,8 @@ pointed message B: { b: Schema.FiniteFromString } }).annotate({}) + strictEqual(schema.tag, "_tag") + const { A, B, C } = schema.cases // cases diff --git a/repos/effect/packages/effect/test/schema/SchemaAOTCompiler.test.ts b/packages/effect/test/schema/SchemaAOTCompiler.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaAOTCompiler.test.ts rename to packages/effect/test/schema/SchemaAOTCompiler.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaAOTCompilerBuild.test.ts b/packages/effect/test/schema/SchemaAOTCompilerBuild.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaAOTCompilerBuild.test.ts rename to packages/effect/test/schema/SchemaAOTCompilerBuild.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaAST.test.ts b/packages/effect/test/schema/SchemaAST.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaAST.test.ts rename to packages/effect/test/schema/SchemaAST.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerApi.test.ts b/packages/effect/test/schema/SchemaCompilerApi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaCompilerApi.test.ts rename to packages/effect/test/schema/SchemaCompilerApi.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerArray.test.ts b/packages/effect/test/schema/SchemaCompilerArray.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaCompilerArray.test.ts rename to packages/effect/test/schema/SchemaCompilerArray.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts b/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts similarity index 58% rename from repos/effect/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts rename to packages/effect/test/schema/SchemaCompilerConcurrency.test.ts index cc7437315..0aebabd27 100644 --- a/repos/effect/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts +++ b/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts @@ -3,6 +3,44 @@ import { Deferred, Effect, Fiber, Schema, SchemaParser } from "effect" import { SchemaJITCompiler } from "effect/schema" describe("compiled construction concurrency", () => { + it.effect("isolates concurrent executions without replaying the eager prefix", () => + Effect.gen(function*() { + const started = yield* Deferred.make() + let prefixReads = 0 + let ids = 0 + let tails = 0 + const schema = Schema.Struct({ + prefix: Schema.String, + id: Schema.Number.pipe(Schema.withConstructorDefault(Effect.gen(function*() { + const id = ++ids + if (id === 2) yield* Deferred.succeed(started, undefined) + yield* Deferred.await(started) + return id + }))), + tail: Schema.String.pipe(Schema.withConstructorDefault(Effect.sync(() => { + tails++ + return "tail" + }))) + }) + SchemaJITCompiler.enable(schema.ast) + const program = SchemaParser.makeEffect(schema)({ + get prefix() { + prefixReads++ + return "prefix" + } + }) + assert.strictEqual(prefixReads, 1) + const results = yield* Effect.all([program, program], { concurrency: "unbounded" }) + assert.deepStrictEqual(results, [ + { prefix: "prefix", id: 1, tail: "tail" }, + { prefix: "prefix", id: 2, tail: "tail" } + ]) + assert.notStrictEqual(results[0], results[1]) + assert.strictEqual(prefixReads, 1) + assert.strictEqual(ids, 2) + assert.strictEqual(tails, 2) + })) + for (const product of ["Struct", "Tuple"] as const) { it.effect(`${product} preserves bounded concurrency and runs defaults once`, () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerConstruction.test.ts b/packages/effect/test/schema/SchemaCompilerConstruction.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaCompilerConstruction.test.ts rename to packages/effect/test/schema/SchemaCompilerConstruction.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerRegression.test.ts b/packages/effect/test/schema/SchemaCompilerRegression.test.ts similarity index 97% rename from repos/effect/packages/effect/test/schema/SchemaCompilerRegression.test.ts rename to packages/effect/test/schema/SchemaCompilerRegression.test.ts index 2241bd8d3..91f0f7c82 100644 --- a/repos/effect/packages/effect/test/schema/SchemaCompilerRegression.test.ts +++ b/packages/effect/test/schema/SchemaCompilerRegression.test.ts @@ -15,6 +15,22 @@ import { SchemaCompiler, SchemaJITCompiler } from "effect/schema" import { deepStrictEqual, strictEqual } from "../utils/assert.ts" describe("compiler regression contracts", () => { + it.effect("re-running a compiled Struct decoder keeps earlier results intact", () => + Effect.gen(function*() { + let ids = 0 + const schema = Schema.Struct({ + id: Schema.Number.pipe(Schema.decode({ + decode: SchemaGetter.transformEffect(() => Effect.sync(() => ++ids)), + encode: SchemaGetter.passthrough() + })) + }) + SchemaJITCompiler.enable(schema.ast) + const program = SchemaParser.decodeUnknownEffect(schema)({ id: 0 }) + const first = yield* program + const second = yield* program + assert.deepStrictEqual([first, second], [{ id: 1 }, { id: 2 }]) + })) + it("preserves template literal issues after compilation", () => { const schema = Schema.TemplateLiteral(["count:", Schema.Int.check(Schema.isGreaterThan(0))]) const inputs = ["count:1", "count:0", "count:1.5", "invalid", null] diff --git a/repos/effect/packages/effect/test/schema/SchemaCompilerStartup.test.ts b/packages/effect/test/schema/SchemaCompilerStartup.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaCompilerStartup.test.ts rename to packages/effect/test/schema/SchemaCompilerStartup.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaGetter.test.ts b/packages/effect/test/schema/SchemaGetter.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaGetter.test.ts rename to packages/effect/test/schema/SchemaGetter.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaIssue.test.ts b/packages/effect/test/schema/SchemaIssue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaIssue.test.ts rename to packages/effect/test/schema/SchemaIssue.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaJITCompiler.test.ts b/packages/effect/test/schema/SchemaJITCompiler.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaJITCompiler.test.ts rename to packages/effect/test/schema/SchemaJITCompiler.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts b/packages/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts rename to packages/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts b/packages/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts rename to packages/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaJITCompilerFallback.test.ts b/packages/effect/test/schema/SchemaJITCompilerFallback.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaJITCompilerFallback.test.ts rename to packages/effect/test/schema/SchemaJITCompilerFallback.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaParser.test.ts b/packages/effect/test/schema/SchemaParser.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaParser.test.ts rename to packages/effect/test/schema/SchemaParser.test.ts diff --git a/repos/effect/packages/effect/test/schema/SchemaTransformation.test.ts b/packages/effect/test/schema/SchemaTransformation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/SchemaTransformation.test.ts rename to packages/effect/test/schema/SchemaTransformation.test.ts diff --git a/repos/effect/packages/effect/test/schema/TemplateLiteral.test.ts b/packages/effect/test/schema/TemplateLiteral.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/TemplateLiteral.test.ts rename to packages/effect/test/schema/TemplateLiteral.test.ts diff --git a/repos/effect/packages/effect/test/schema/VariantSchema.test.ts b/packages/effect/test/schema/VariantSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/VariantSchema.test.ts rename to packages/effect/test/schema/VariantSchema.test.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/HMR-Class.ts b/packages/effect/test/schema/fixtures/HMR-Class.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/HMR-Class.ts rename to packages/effect/test/schema/fixtures/HMR-Class.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/HMR-sanity-check.ts b/packages/effect/test/schema/fixtures/HMR-sanity-check.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/HMR-sanity-check.ts rename to packages/effect/test/schema/fixtures/HMR-sanity-check.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/aot-build.ts b/packages/effect/test/schema/fixtures/aot-build.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/aot-build.ts rename to packages/effect/test/schema/fixtures/aot-build.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/aot-import-guard.ts b/packages/effect/test/schema/fixtures/aot-import-guard.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/aot-import-guard.ts rename to packages/effect/test/schema/fixtures/aot-import-guard.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/aot-runner.ts b/packages/effect/test/schema/fixtures/aot-runner.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/aot-runner.ts rename to packages/effect/test/schema/fixtures/aot-runner.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/aot.ts b/packages/effect/test/schema/fixtures/aot.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/aot.ts rename to packages/effect/test/schema/fixtures/aot.ts diff --git a/repos/effect/packages/effect/test/schema/fixtures/construction.ts b/packages/effect/test/schema/fixtures/construction.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/fixtures/construction.ts rename to packages/effect/test/schema/fixtures/construction.ts diff --git a/repos/effect/packages/effect/test/schema/jsonSchemaRoundTrip.test.ts b/packages/effect/test/schema/jsonSchemaRoundTrip.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/jsonSchemaRoundTrip.test.ts rename to packages/effect/test/schema/jsonSchemaRoundTrip.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/builtInRevivers.test.ts b/packages/effect/test/schema/representation/builtInRevivers.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/builtInRevivers.test.ts rename to packages/effect/test/schema/representation/builtInRevivers.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromJson.test.ts b/packages/effect/test/schema/representation/fromJson.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromJson.test.ts rename to packages/effect/test/schema/representation/fromJson.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromJsonMultiDocument.test.ts b/packages/effect/test/schema/representation/fromJsonMultiDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromJsonMultiDocument.test.ts rename to packages/effect/test/schema/representation/fromJsonMultiDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts b/packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts rename to packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts b/packages/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts rename to packages/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromRepresentation.test.ts b/packages/effect/test/schema/representation/fromRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromRepresentation.test.ts rename to packages/effect/test/schema/representation/fromRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/fromRepresentations.test.ts b/packages/effect/test/schema/representation/fromRepresentations.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/fromRepresentations.test.ts rename to packages/effect/test/schema/representation/fromRepresentations.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/makeCode.test.ts b/packages/effect/test/schema/representation/makeCode.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/makeCode.test.ts rename to packages/effect/test/schema/representation/makeCode.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts b/packages/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts rename to packages/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/stringTreeRoundtrip.test.ts b/packages/effect/test/schema/representation/stringTreeRoundtrip.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/stringTreeRoundtrip.test.ts rename to packages/effect/test/schema/representation/stringTreeRoundtrip.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toCodeDocument.annotations.test.ts b/packages/effect/test/schema/representation/toCodeDocument.annotations.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toCodeDocument.annotations.test.ts rename to packages/effect/test/schema/representation/toCodeDocument.annotations.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toCodeDocument.test.ts b/packages/effect/test/schema/representation/toCodeDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toCodeDocument.test.ts rename to packages/effect/test/schema/representation/toCodeDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toJson.test.ts b/packages/effect/test/schema/representation/toJson.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toJson.test.ts rename to packages/effect/test/schema/representation/toJson.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toJsonMultiDocument.test.ts b/packages/effect/test/schema/representation/toJsonMultiDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toJsonMultiDocument.test.ts rename to packages/effect/test/schema/representation/toJsonMultiDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts b/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts rename to packages/effect/test/schema/representation/toJsonSchemaDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts b/packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts rename to packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toMultiDocument.test.ts b/packages/effect/test/schema/representation/toMultiDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toMultiDocument.test.ts rename to packages/effect/test/schema/representation/toMultiDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toRepresentation.test.ts b/packages/effect/test/schema/representation/toRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toRepresentation.test.ts rename to packages/effect/test/schema/representation/toRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/schema/representation/toRepresentations.test.ts b/packages/effect/test/schema/representation/toRepresentations.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/representation/toRepresentations.test.ts rename to packages/effect/test/schema/representation/toRepresentations.test.ts diff --git a/repos/effect/packages/effect/test/schema/toCodec.test.ts b/packages/effect/test/schema/toCodec.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/toCodec.test.ts rename to packages/effect/test/schema/toCodec.test.ts diff --git a/repos/effect/packages/effect/test/schema/toDifferJsonPatch.test.ts b/packages/effect/test/schema/toDifferJsonPatch.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/toDifferJsonPatch.test.ts rename to packages/effect/test/schema/toDifferJsonPatch.test.ts diff --git a/repos/effect/packages/effect/test/schema/toEquivalence.test.ts b/packages/effect/test/schema/toEquivalence.test.ts similarity index 93% rename from repos/effect/packages/effect/test/schema/toEquivalence.test.ts rename to packages/effect/test/schema/toEquivalence.test.ts index 5d34dd2a3..8b88901c5 100644 --- a/repos/effect/packages/effect/test/schema/toEquivalence.test.ts +++ b/packages/effect/test/schema/toEquivalence.test.ts @@ -4,6 +4,7 @@ import { Duration, Equivalence, HashMap, + HashSet, Option, Redacted, Result, @@ -537,6 +538,15 @@ describe("toEquivalence", () => { assertFalse(equivalence(new Set([0, 1]), new Set([2, 2]))) }) + it.each([0, 1])("ReadonlySet rejects a proper subset of size %i in both orders", (size) => { + const equivalence = Schema.toEquivalence(Schema.ReadonlySet(Modulo2)) + const subset = new Set([0, 1].slice(0, size)) + const superset = new Set([0, 1]) + + assertFalse(equivalence(superset, subset)) + assertFalse(equivalence(subset, superset)) + }) + it("ReadonlyMap(Modulo2, Modulo3)", () => { const schema = Schema.ReadonlyMap(Modulo2, Modulo3) const equivalence = Schema.toEquivalence(schema) @@ -553,6 +563,25 @@ describe("toEquivalence", () => { assertFalse(equivalence(new Map([[0, 1], [1, 2]]), new Map([[0, 1], [2, 2]]))) }) + it.each([0, 1])("ReadonlyMap rejects a proper subset of size %i in both orders", (size) => { + const equivalence = Schema.toEquivalence(Schema.ReadonlyMap(Modulo2, Modulo3)) + const entries: Array<[number, number]> = [[0, 1], [1, 2]] + const subset = new Map(entries.slice(0, size)) + const superset = new Map(entries) + + assertFalse(equivalence(superset, subset)) + assertFalse(equivalence(subset, superset)) + }) + + it.each([0, 1])("HashSet rejects a proper subset of size %i in both orders", (size) => { + const equivalence = Schema.toEquivalence(Schema.HashSet(Modulo2)) + const subset = HashSet.fromIterable([0, 1].slice(0, size)) + const superset = HashSet.make(0, 1) + + assertFalse(equivalence(superset, subset)) + assertFalse(equivalence(subset, superset)) + }) + it("HashMap(Modulo2, Modulo3)", () => { const schema = Schema.HashMap(Modulo2, Modulo3) const equivalence = Schema.toEquivalence(schema) @@ -569,6 +598,16 @@ describe("toEquivalence", () => { assertFalse(equivalence(HashMap.make([0, 1], [1, 2]), HashMap.make([0, 1], [2, 2]))) }) + it.each([0, 1])("HashMap rejects a proper subset of size %i in both orders", (size) => { + const equivalence = Schema.toEquivalence(Schema.HashMap(Modulo2, Modulo3)) + const entries: Array<[number, number]> = [[0, 1], [1, 2]] + const subset = HashMap.fromIterable(entries.slice(0, size)) + const superset = HashMap.fromIterable(entries) + + assertFalse(equivalence(superset, subset)) + assertFalse(equivalence(subset, superset)) + }) + it("Duration", () => { const schema = Schema.Duration const equivalence = Schema.toEquivalence(schema) diff --git a/repos/effect/packages/effect/test/schema/toFormatter.test.ts b/packages/effect/test/schema/toFormatter.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/toFormatter.test.ts rename to packages/effect/test/schema/toFormatter.test.ts diff --git a/repos/effect/packages/effect/test/schema/toIso.test.ts b/packages/effect/test/schema/toIso.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/toIso.test.ts rename to packages/effect/test/schema/toIso.test.ts diff --git a/repos/effect/packages/effect/test/schema/toJsonSchemaDocument.test.ts b/packages/effect/test/schema/toJsonSchemaDocument.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/toJsonSchemaDocument.test.ts rename to packages/effect/test/schema/toJsonSchemaDocument.test.ts diff --git a/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts b/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts similarity index 80% rename from repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts rename to packages/effect/test/schema/toStandardJSONSchemaV1.test.ts index f57a476e7..6e4637e4a 100644 --- a/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts +++ b/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts @@ -22,6 +22,36 @@ function standardConvertToJSONSchemaOutput( } describe("toStandardJSONSchemaV1", () => { + it("describes subclass fields when the parent already has a Standard JSON Schema adapter", () => { + class Parent extends Schema.Class("Parent")({ a: Schema.String }) {} + Schema.toStandardJSONSchemaV1(Parent) + class Child extends Parent.extend("Child")({ b: Schema.FiniteFromString }) {} + const child = Schema.toStandardJSONSchemaV1(Child) + + deepStrictEqual(standardConvertToJSONSchemaInput(child), { + $ref: "#/$defs/Child", + $defs: { + Child: { + type: "object", + properties: { a: { type: "string" }, b: { type: "string" } }, + required: ["a", "b"], + additionalProperties: true + } + } + }) + deepStrictEqual(standardConvertToJSONSchemaOutput(child), { + $ref: "#/$defs/Child", + $defs: { + Child: { + type: "object", + properties: { a: { type: "string" }, b: { type: "number" } }, + required: ["a", "b"], + additionalProperties: true + } + } + }) + }) + it("should return a schema with Standard JSON Schema metadata", () => { const schema = Schema.FiniteFromString const standardSchema = Schema.toStandardJSONSchemaV1(schema) diff --git a/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts b/packages/effect/test/schema/toStandardSchemaV1.test.ts similarity index 96% rename from repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts rename to packages/effect/test/schema/toStandardSchemaV1.test.ts index 5521a68ec..991354948 100644 --- a/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts +++ b/packages/effect/test/schema/toStandardSchemaV1.test.ts @@ -102,6 +102,17 @@ const AsyncString = Schema.String.pipe(Schema.decode({ const AsyncNonEmptyString = AsyncString.check(Schema.isNonEmpty()) describe("toStandardSchemaV1", () => { + it("validates subclass fields when the parent already has a Standard Schema adapter", () => { + class Parent extends Schema.Class("Parent")({ a: Schema.String }) {} + const parent = Schema.toStandardSchemaV1(Parent) + class Child extends Parent.extend("Child")({ b: Schema.Number }) {} + const child = Schema.toStandardSchemaV1(Child) + + expectSyncSuccess(parent, { a: "a" }, new Parent({ a: "a" })) + expectSyncFailure(child, { a: "a" }, [{ path: ["b"], message: "Missing key" }]) + expectSyncSuccess(child, { a: "a", b: 1 }, new Child({ a: "a", b: 1 })) + }) + it("should return a Standard Schema V1 schema", () => { const schema = Schema.FiniteFromString const standardSchema = Schema.toStandardSchemaV1(schema) diff --git a/repos/effect/packages/effect/test/schema/v3-v4.test.ts b/packages/effect/test/schema/v3-v4.test.ts similarity index 100% rename from repos/effect/packages/effect/test/schema/v3-v4.test.ts rename to packages/effect/test/schema/v3-v4.test.ts diff --git a/repos/effect/packages/effect/test/socket/Socket.test.ts b/packages/effect/test/socket/Socket.test.ts similarity index 100% rename from repos/effect/packages/effect/test/socket/Socket.test.ts rename to packages/effect/test/socket/Socket.test.ts diff --git a/repos/effect/packages/effect/test/sql/SqlClient.test.ts b/packages/effect/test/sql/SqlClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/sql/SqlClient.test.ts rename to packages/effect/test/sql/SqlClient.test.ts diff --git a/repos/effect/packages/effect/test/sql/SqlError.test.ts b/packages/effect/test/sql/SqlError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/sql/SqlError.test.ts rename to packages/effect/test/sql/SqlError.test.ts diff --git a/repos/effect/packages/effect/test/sql/SqlResolver.test.ts b/packages/effect/test/sql/SqlResolver.test.ts similarity index 100% rename from repos/effect/packages/effect/test/sql/SqlResolver.test.ts rename to packages/effect/test/sql/SqlResolver.test.ts diff --git a/repos/effect/packages/effect/test/sql/SqlSchema.test.ts b/packages/effect/test/sql/SqlSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/sql/SqlSchema.test.ts rename to packages/effect/test/sql/SqlSchema.test.ts diff --git a/repos/effect/packages/effect/test/sql/Statement.test.ts b/packages/effect/test/sql/Statement.test.ts similarity index 100% rename from repos/effect/packages/effect/test/sql/Statement.test.ts rename to packages/effect/test/sql/Statement.test.ts diff --git a/repos/effect/packages/effect/test/testing/TestSchema.test.ts b/packages/effect/test/testing/TestSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/testing/TestSchema.test.ts rename to packages/effect/test/testing/TestSchema.test.ts diff --git a/repos/effect/packages/effect/test/utils/assert.ts b/packages/effect/test/utils/assert.ts similarity index 100% rename from repos/effect/packages/effect/test/utils/assert.ts rename to packages/effect/test/utils/assert.ts diff --git a/repos/effect/packages/effect/test/utils/chunkCoordination.ts b/packages/effect/test/utils/chunkCoordination.ts similarity index 100% rename from repos/effect/packages/effect/test/utils/chunkCoordination.ts rename to packages/effect/test/utils/chunkCoordination.ts diff --git a/repos/effect/packages/effect/test/utils/counter.ts b/packages/effect/test/utils/counter.ts similarity index 100% rename from repos/effect/packages/effect/test/utils/counter.ts rename to packages/effect/test/utils/counter.ts diff --git a/repos/effect/packages/effect/test/utils/gc.ts b/packages/effect/test/utils/gc.ts similarity index 100% rename from repos/effect/packages/effect/test/utils/gc.ts rename to packages/effect/test/utils/gc.ts diff --git a/repos/effect/packages/effect/test/workers/Worker.test.ts b/packages/effect/test/workers/Worker.test.ts similarity index 100% rename from repos/effect/packages/effect/test/workers/Worker.test.ts rename to packages/effect/test/workers/Worker.test.ts diff --git a/repos/effect/packages/effect/test/workers/WorkerError.test.ts b/packages/effect/test/workers/WorkerError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/workers/WorkerError.test.ts rename to packages/effect/test/workers/WorkerError.test.ts diff --git a/repos/effect/packages/effect/test/workflow/DurableQueue.test.ts b/packages/effect/test/workflow/DurableQueue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/workflow/DurableQueue.test.ts rename to packages/effect/test/workflow/DurableQueue.test.ts diff --git a/repos/effect/packages/effect/test/workflow/Workflow.test.ts b/packages/effect/test/workflow/Workflow.test.ts similarity index 100% rename from repos/effect/packages/effect/test/workflow/Workflow.test.ts rename to packages/effect/test/workflow/Workflow.test.ts diff --git a/repos/effect/packages/effect/test/workflow/WorkflowEngine.test.ts b/packages/effect/test/workflow/WorkflowEngine.test.ts similarity index 100% rename from repos/effect/packages/effect/test/workflow/WorkflowEngine.test.ts rename to packages/effect/test/workflow/WorkflowEngine.test.ts diff --git a/repos/effect/packages/effect/tsconfig.json b/packages/effect/tsconfig.json similarity index 100% rename from repos/effect/packages/effect/tsconfig.json rename to packages/effect/tsconfig.json diff --git a/repos/effect/packages/effect/typeperf/README.md b/packages/effect/typeperf/README.md similarity index 100% rename from repos/effect/packages/effect/typeperf/README.md rename to packages/effect/typeperf/README.md diff --git a/repos/effect/packages/effect/typeperf/compare.mjs b/packages/effect/typeperf/compare.mjs similarity index 100% rename from repos/effect/packages/effect/typeperf/compare.mjs rename to packages/effect/typeperf/compare.mjs diff --git a/repos/effect/packages/effect/typeperf/compare/httpapi.mjs b/packages/effect/typeperf/compare/httpapi.mjs similarity index 100% rename from repos/effect/packages/effect/typeperf/compare/httpapi.mjs rename to packages/effect/typeperf/compare/httpapi.mjs diff --git a/repos/effect/packages/effect/typeperf/config.json b/packages/effect/typeperf/config.json similarity index 100% rename from repos/effect/packages/effect/typeperf/config.json rename to packages/effect/typeperf/config.json diff --git a/repos/effect/packages/effect/typeperf/run.mjs b/packages/effect/typeperf/run.mjs similarity index 100% rename from repos/effect/packages/effect/typeperf/run.mjs rename to packages/effect/typeperf/run.mjs diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/baseline.ts b/packages/effect/typeperf/suites/httpapi/baseline.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/baseline.ts rename to packages/effect/typeperf/suites/httpapi/baseline.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts b/packages/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts b/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts b/packages/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts b/packages/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts b/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts rename to packages/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts diff --git a/repos/effect/packages/effect/typeperf/suites/httpapi/thresholds.json b/packages/effect/typeperf/suites/httpapi/thresholds.json similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/httpapi/thresholds.json rename to packages/effect/typeperf/suites/httpapi/thresholds.json diff --git a/repos/effect/packages/effect/typeperf/suites/schema/baseline.ts b/packages/effect/typeperf/suites/schema/baseline.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/baseline.ts rename to packages/effect/typeperf/suites/schema/baseline.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/array.ts b/packages/effect/typeperf/suites/schema/fixtures/array.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/array.ts rename to packages/effect/typeperf/suites/schema/fixtures/array.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/brand.ts b/packages/effect/typeperf/suites/schema/fixtures/brand.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/brand.ts rename to packages/effect/typeperf/suites/schema/fixtures/brand.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts b/packages/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts rename to packages/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts b/packages/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts rename to packages/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/exit.ts b/packages/effect/typeperf/suites/schema/fixtures/exit.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/exit.ts rename to packages/effect/typeperf/suites/schema/fixtures/exit.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/non-empty-array.ts b/packages/effect/typeperf/suites/schema/fixtures/non-empty-array.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/non-empty-array.ts rename to packages/effect/typeperf/suites/schema/fixtures/non-empty-array.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/record.ts b/packages/effect/typeperf/suites/schema/fixtures/record.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/record.ts rename to packages/effect/typeperf/suites/schema/fixtures/record.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-mixed.ts b/packages/effect/typeperf/suites/schema/fixtures/struct-mixed.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-mixed.ts rename to packages/effect/typeperf/suites/schema/fixtures/struct-mixed.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts b/packages/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts rename to packages/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts b/packages/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts rename to packages/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-required.ts b/packages/effect/typeperf/suites/schema/fixtures/struct-required.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-required.ts rename to packages/effect/typeperf/suites/schema/fixtures/struct-required.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts b/packages/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts rename to packages/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/tagged-union.ts b/packages/effect/typeperf/suites/schema/fixtures/tagged-union.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/tagged-union.ts rename to packages/effect/typeperf/suites/schema/fixtures/tagged-union.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts b/packages/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-json.ts b/packages/effect/typeperf/suites/schema/fixtures/to-codec-json.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-json.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-codec-json.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts b/packages/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-encoded.ts b/packages/effect/typeperf/suites/schema/fixtures/to-encoded.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-encoded.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-encoded.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts b/packages/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-type.ts b/packages/effect/typeperf/suites/schema/fixtures/to-type.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/to-type.ts rename to packages/effect/typeperf/suites/schema/fixtures/to-type.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts b/packages/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts rename to packages/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/tuple.ts b/packages/effect/typeperf/suites/schema/fixtures/tuple.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/tuple.ts rename to packages/effect/typeperf/suites/schema/fixtures/tuple.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/fixtures/union.ts b/packages/effect/typeperf/suites/schema/fixtures/union.ts similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/fixtures/union.ts rename to packages/effect/typeperf/suites/schema/fixtures/union.ts diff --git a/repos/effect/packages/effect/typeperf/suites/schema/thresholds.json b/packages/effect/typeperf/suites/schema/thresholds.json similarity index 100% rename from repos/effect/packages/effect/typeperf/suites/schema/thresholds.json rename to packages/effect/typeperf/suites/schema/thresholds.json diff --git a/repos/effect/packages/effect/typetest/AiError.tst.ts b/packages/effect/typetest/AiError.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/AiError.tst.ts rename to packages/effect/typetest/AiError.tst.ts diff --git a/repos/effect/packages/effect/typetest/Array.tst.ts b/packages/effect/typetest/Array.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Array.tst.ts rename to packages/effect/typetest/Array.tst.ts diff --git a/repos/effect/packages/effect/typetest/Brand.tst.ts b/packages/effect/typetest/Brand.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Brand.tst.ts rename to packages/effect/typetest/Brand.tst.ts diff --git a/repos/effect/packages/effect/typetest/ByteSize.tst.ts b/packages/effect/typetest/ByteSize.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ByteSize.tst.ts rename to packages/effect/typetest/ByteSize.tst.ts diff --git a/repos/effect/packages/effect/typetest/Channel.tst.ts b/packages/effect/typetest/Channel.tst.ts similarity index 70% rename from repos/effect/packages/effect/typetest/Channel.tst.ts rename to packages/effect/typetest/Channel.tst.ts index 6031c4f99..8f0662bbd 100644 --- a/repos/effect/packages/effect/typetest/Channel.tst.ts +++ b/packages/effect/typetest/Channel.tst.ts @@ -1,10 +1,15 @@ -import { Channel, Data, type Effect, pipe, Result } from "effect" +import { Channel, Data, type Effect, pipe, type PubSub, Result } from "effect" import { describe, expect, it } from "tstyche" class ErrorA extends Data.TaggedError("ErrorA")<{ readonly message: string }> {} class ErrorB extends Data.TaggedError("ErrorB")<{ readonly code: number }> {} declare const channel: Channel.Channel +declare const pubsub: PubSub.PubSub +interface Dependency { + readonly Dependency: unique symbol +} +declare const pubsubChannel: Channel.Channel class RateLimit extends Data.TaggedError("RateLimit")<{ readonly retryAfter: number }> {} class Quota extends Data.TaggedError("Quota")<{ readonly limit: number }> {} @@ -128,3 +133,40 @@ describe("Channel.runCount", () => { expect(Channel.runCount(Channel.fromIterable([1, 2, 3]))).type.toBe>() }) }) + +interface Config { + readonly _: unique symbol +} +interface Db { + readonly _: unique symbol +} +declare const dbChannel: Channel.Channel +declare const serviceFreeChannel: Channel.Channel +declare const parseWithConfig: (s: string) => Effect.Effect + +describe("Channel.mapInput", () => { + it("adds mapper requirements and preserves channel requirements in data-last usage", () => { + const serviceFreeResult = pipe(serviceFreeChannel, Channel.mapInput(parseWithConfig)) + expect(serviceFreeResult).type.toBe>() + + const result = pipe(dbChannel, Channel.mapInput(parseWithConfig)) + expect(result).type.toBe>() + }) + + it("adds mapper requirements and preserves channel requirements in data-first usage", () => { + const serviceFreeResult = Channel.mapInput(serviceFreeChannel, parseWithConfig) + expect(serviceFreeResult).type.toBe>() + + const result = Channel.mapInput(dbChannel, parseWithConfig) + expect(result).type.toBe>() + }) +}) + +describe("Channel.runIntoPubSub", () => { + it("preserves the channel error and environment in both overloads", () => { + expect(Channel.runIntoPubSub(pubsubChannel, pubsub)).type.toBe>() + expect(pipe(pubsubChannel, Channel.runIntoPubSub(pubsub))).type.toBe< + Effect.Effect + >() + }) +}) diff --git a/repos/effect/packages/effect/typetest/ChannelSchema.tst.ts b/packages/effect/typetest/ChannelSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ChannelSchema.tst.ts rename to packages/effect/typetest/ChannelSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/Clock.tst.ts b/packages/effect/typetest/Clock.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Clock.tst.ts rename to packages/effect/typetest/Clock.tst.ts diff --git a/repos/effect/packages/effect/typetest/Config.tst.ts b/packages/effect/typetest/Config.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Config.tst.ts rename to packages/effect/typetest/Config.tst.ts diff --git a/repos/effect/packages/effect/typetest/ConfigProvider.tst.ts b/packages/effect/typetest/ConfigProvider.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ConfigProvider.tst.ts rename to packages/effect/typetest/ConfigProvider.tst.ts diff --git a/repos/effect/packages/effect/typetest/Context.tst.ts b/packages/effect/typetest/Context.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Context.tst.ts rename to packages/effect/typetest/Context.tst.ts diff --git a/repos/effect/packages/effect/typetest/Data.tst.ts b/packages/effect/typetest/Data.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Data.tst.ts rename to packages/effect/typetest/Data.tst.ts diff --git a/repos/effect/packages/effect/typetest/Effect.tst.ts b/packages/effect/typetest/Effect.tst.ts similarity index 95% rename from repos/effect/packages/effect/typetest/Effect.tst.ts rename to packages/effect/typetest/Effect.tst.ts index 7efb01d51..1a4e6f7b4 100644 --- a/repos/effect/packages/effect/typetest/Effect.tst.ts +++ b/packages/effect/typetest/Effect.tst.ts @@ -6,11 +6,11 @@ import { Data, Duration, Effect, - type ExecutionPlan, + ExecutionPlan, Exit, Fiber, HashMap, - type Layer, + Layer, Metric, type Option, pipe, @@ -1439,6 +1439,44 @@ describe("Effect.withExecutionPlan", () => { const result = Effect.withExecutionPlan(self, plan) expect(result).type.toBe>() }) + + it("tracks predicate errors and requirements with an unconditional fallback", () => { + class Dep extends Context.Service()("Dep") {} + const predicatePlan = ExecutionPlan.make({ + provide: Layer.empty, + while: () => Effect.flatMap(Dep, () => Effect.fail("predicate-failed" as const)) + }, { provide: Layer.empty }) + const result = Effect.withExecutionPlan(Effect.fail("operation-failed" as const), predicatePlan) + expect(result).type.toBe>() + }) + + it("tracks layer requirements with a context fallback", () => { + class Dep extends Context.Service()("Dep") {} + const layerPlan = ExecutionPlan.make({ + provide: Layer.effect(Dep, Effect.flatMap(Dep, () => Effect.fail("layer-failed" as const))) + }, { provide: Context.empty() }) + const result = Effect.withExecutionPlan(Effect.fail("operation-failed" as const), layerPlan) + expect(result).type.toBe>() + }) + + it("tracks schedule errors and requirements with an unscheduled fallback", () => { + class PolicyService extends Context.Service()("PolicyService") {} + const schedulePlan = ExecutionPlan.make({ + provide: Layer.empty, + schedule: Schedule.map( + Schedule.forever, + () => Effect.flatMap(PolicyService, () => Effect.fail("policy-failed" as const)) + ) + }, { provide: Layer.empty }) + const result = Effect.withExecutionPlan(Effect.fail("operation-failed" as const), schedulePlan) + expect(result).type.toBe>() + const capturedResult = Effect.flatMap(schedulePlan.captureRequirements, (captured) => { + const result = Effect.withExecutionPlan(Effect.fail("operation-failed" as const), captured) + expect(result).type.toBe>() + return result + }) + expect(capturedResult).type.toBe>() + }) }) describe("Effect.cachedWithTTL", () => { diff --git a/repos/effect/packages/effect/typetest/Effectable.tst.ts b/packages/effect/typetest/Effectable.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Effectable.tst.ts rename to packages/effect/typetest/Effectable.tst.ts diff --git a/repos/effect/packages/effect/typetest/Equivalence.tst.ts b/packages/effect/typetest/Equivalence.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Equivalence.tst.ts rename to packages/effect/typetest/Equivalence.tst.ts diff --git a/repos/effect/packages/effect/typetest/Fiber.tst.ts b/packages/effect/typetest/Fiber.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Fiber.tst.ts rename to packages/effect/typetest/Fiber.tst.ts diff --git a/repos/effect/packages/effect/typetest/FileSystem.tst.ts b/packages/effect/typetest/FileSystem.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/FileSystem.tst.ts rename to packages/effect/typetest/FileSystem.tst.ts diff --git a/repos/effect/packages/effect/typetest/Function.tst.ts b/packages/effect/typetest/Function.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Function.tst.ts rename to packages/effect/typetest/Function.tst.ts diff --git a/repos/effect/packages/effect/typetest/Graph.tst.ts b/packages/effect/typetest/Graph.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Graph.tst.ts rename to packages/effect/typetest/Graph.tst.ts diff --git a/repos/effect/packages/effect/typetest/Iterable.tst.ts b/packages/effect/typetest/Iterable.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Iterable.tst.ts rename to packages/effect/typetest/Iterable.tst.ts diff --git a/repos/effect/packages/effect/typetest/Layer.tst.ts b/packages/effect/typetest/Layer.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Layer.tst.ts rename to packages/effect/typetest/Layer.tst.ts diff --git a/repos/effect/packages/effect/typetest/Match.tst.ts b/packages/effect/typetest/Match.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Match.tst.ts rename to packages/effect/typetest/Match.tst.ts diff --git a/repos/effect/packages/effect/typetest/MigratedSubpaths.tst.ts b/packages/effect/typetest/MigratedSubpaths.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/MigratedSubpaths.tst.ts rename to packages/effect/typetest/MigratedSubpaths.tst.ts diff --git a/repos/effect/packages/effect/typetest/Newtype.tst.ts b/packages/effect/typetest/Newtype.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Newtype.tst.ts rename to packages/effect/typetest/Newtype.tst.ts diff --git a/repos/effect/packages/effect/typetest/Optic.tst.ts b/packages/effect/typetest/Optic.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Optic.tst.ts rename to packages/effect/typetest/Optic.tst.ts diff --git a/repos/effect/packages/effect/typetest/Option.tst.ts b/packages/effect/typetest/Option.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Option.tst.ts rename to packages/effect/typetest/Option.tst.ts diff --git a/repos/effect/packages/effect/typetest/Order.tst.ts b/packages/effect/typetest/Order.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Order.tst.ts rename to packages/effect/typetest/Order.tst.ts diff --git a/repos/effect/packages/effect/typetest/Predicate.tst.ts b/packages/effect/typetest/Predicate.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Predicate.tst.ts rename to packages/effect/typetest/Predicate.tst.ts diff --git a/repos/effect/packages/effect/typetest/Queue.tst.ts b/packages/effect/typetest/Queue.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Queue.tst.ts rename to packages/effect/typetest/Queue.tst.ts diff --git a/repos/effect/packages/effect/typetest/Random.tst.ts b/packages/effect/typetest/Random.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Random.tst.ts rename to packages/effect/typetest/Random.tst.ts diff --git a/repos/effect/packages/effect/typetest/Record.tst.ts b/packages/effect/typetest/Record.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Record.tst.ts rename to packages/effect/typetest/Record.tst.ts diff --git a/repos/effect/packages/effect/typetest/Result.tst.ts b/packages/effect/typetest/Result.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Result.tst.ts rename to packages/effect/typetest/Result.tst.ts diff --git a/repos/effect/packages/effect/typetest/Schedule.tst.ts b/packages/effect/typetest/Schedule.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Schedule.tst.ts rename to packages/effect/typetest/Schedule.tst.ts diff --git a/repos/effect/packages/effect/typetest/Scope.tst.ts b/packages/effect/typetest/Scope.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Scope.tst.ts rename to packages/effect/typetest/Scope.tst.ts diff --git a/repos/effect/packages/effect/typetest/Sink.tst.ts b/packages/effect/typetest/Sink.tst.ts similarity index 59% rename from repos/effect/packages/effect/typetest/Sink.tst.ts rename to packages/effect/typetest/Sink.tst.ts index 4ed4dd6b3..c5dff8cc8 100644 --- a/repos/effect/packages/effect/typetest/Sink.tst.ts +++ b/packages/effect/typetest/Sink.tst.ts @@ -10,4 +10,11 @@ describe("Sink", () => { expect(recovered).type.toBe>() }) + + it("curried catchCause replaces the handled error type", () => { + const sink = null as unknown as Sink.Sink + const recovered = sink.pipe(Sink.catchCause((_) => Effect.fail("new-error" as const))) + + expect(recovered).type.toBe>() + }) }) diff --git a/repos/effect/packages/effect/typetest/Stream.tst.ts b/packages/effect/typetest/Stream.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Stream.tst.ts rename to packages/effect/typetest/Stream.tst.ts diff --git a/repos/effect/packages/effect/typetest/Struct.tst.ts b/packages/effect/typetest/Struct.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Struct.tst.ts rename to packages/effect/typetest/Struct.tst.ts diff --git a/repos/effect/packages/effect/typetest/SynchronizedRef.tst.ts b/packages/effect/typetest/SynchronizedRef.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/SynchronizedRef.tst.ts rename to packages/effect/typetest/SynchronizedRef.tst.ts diff --git a/repos/effect/packages/effect/typetest/TestSchema.tst.ts b/packages/effect/typetest/TestSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/TestSchema.tst.ts rename to packages/effect/typetest/TestSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/Tuple.tst.ts b/packages/effect/typetest/Tuple.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Tuple.tst.ts rename to packages/effect/typetest/Tuple.tst.ts diff --git a/repos/effect/packages/effect/typetest/Types.tst.ts b/packages/effect/typetest/Types.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/Types.tst.ts rename to packages/effect/typetest/Types.tst.ts diff --git a/repos/effect/packages/effect/typetest/VariantSchema.tst.ts b/packages/effect/typetest/VariantSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/VariantSchema.tst.ts rename to packages/effect/typetest/VariantSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/ai/DecisionModel.tst.ts b/packages/effect/typetest/ai/DecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ai/DecisionModel.tst.ts rename to packages/effect/typetest/ai/DecisionModel.tst.ts diff --git a/repos/effect/packages/effect/typetest/ai/LanguageModel.tst.ts b/packages/effect/typetest/ai/LanguageModel.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ai/LanguageModel.tst.ts rename to packages/effect/typetest/ai/LanguageModel.tst.ts diff --git a/repos/effect/packages/effect/typetest/ai/McpServer.tst.ts b/packages/effect/typetest/ai/McpServer.tst.ts similarity index 80% rename from repos/effect/packages/effect/typetest/ai/McpServer.tst.ts rename to packages/effect/typetest/ai/McpServer.tst.ts index d2f643809..54e972885 100644 --- a/repos/effect/packages/effect/typetest/ai/McpServer.tst.ts +++ b/packages/effect/typetest/ai/McpServer.tst.ts @@ -1,7 +1,9 @@ -import { McpProtocol, McpSchema, McpServer } from "effect/ai" +import { McpProtocol, McpSchema, McpServer, Tool, Toolkit } from "effect/ai" import * as McpProtocolInternal from "effect/ai/internal/mcpProtocol" import type * as Cause from "effect/Cause" -import type * as Effect from "effect/Effect" +import * as Context from "effect/Context" +import * as Effect from "effect/Effect" +import * as HttpClient from "effect/http/HttpClient" import type * as Layer from "effect/Layer" import * as Rpc from "effect/rpc/Rpc" import * as RpcGroup from "effect/rpc/RpcGroup" @@ -141,6 +143,50 @@ describe("McpServer", () => { }) }) + it("should retain toolkit handler services in both registration APIs", () => { + const toolkit = Toolkit.make(Tool.make("http", { dependencies: [HttpClient.HttpClient] })) + const registered = McpServer.registerToolkit(toolkit) + const layer = McpServer.toolkit(toolkit) + type Requirements = Tool.HandlersFor | HttpClient.HttpClient + + expect>().type.toBe() + expect>().type.toBe() + }) + + describe("prompts", () => { + class Lookup extends Context.Service }>()("Lookup") {} + const name = () => Effect.map(Lookup, (lookup) => lookup.values) + + it("should require services used by completion handlers", () => { + const options = { + name: "lookup", + parameters: { name: Schema.String }, + completion: { name }, + content: () => Effect.succeed("content") + } + const registered = McpServer.registerPrompt(options) + const layer = McpServer.prompt(options) + + expect>().type.toBe() + expect>().type.toBe() + }) + + it("should require services used by optional completion handlers", () => { + const completion: { readonly name?: typeof name } = { name } + const options = { + name: "lookup", + parameters: { name: Schema.String }, + completion, + content: () => Effect.succeed("content") + } + const registered = McpServer.registerPrompt(options) + const layer = McpServer.prompt(options) + + expect>().type.toBe() + expect>().type.toBe() + }) + }) + describe("request context", () => { it("should not claim that stateless handlers receive the legacy client service", () => { const layer = McpServer.resource({ diff --git a/repos/effect/packages/effect/typetest/ai/Response.tst.ts b/packages/effect/typetest/ai/Response.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ai/Response.tst.ts rename to packages/effect/typetest/ai/Response.tst.ts diff --git a/repos/effect/packages/effect/typetest/ai/Tool.tst.ts b/packages/effect/typetest/ai/Tool.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ai/Tool.tst.ts rename to packages/effect/typetest/ai/Tool.tst.ts diff --git a/repos/effect/packages/effect/typetest/ai/Toolkit.tst.ts b/packages/effect/typetest/ai/Toolkit.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/ai/Toolkit.tst.ts rename to packages/effect/typetest/ai/Toolkit.tst.ts diff --git a/repos/effect/packages/effect/typetest/arbitrary/Arbitrary.tst.ts b/packages/effect/typetest/arbitrary/Arbitrary.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/arbitrary/Arbitrary.tst.ts rename to packages/effect/typetest/arbitrary/Arbitrary.tst.ts diff --git a/repos/effect/packages/effect/typetest/cli/Command.tst.ts b/packages/effect/typetest/cli/Command.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cli/Command.tst.ts rename to packages/effect/typetest/cli/Command.tst.ts diff --git a/repos/effect/packages/effect/typetest/cli/Param.tst.ts b/packages/effect/typetest/cli/Param.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cli/Param.tst.ts rename to packages/effect/typetest/cli/Param.tst.ts diff --git a/repos/effect/packages/effect/typetest/cli/Prompt.tst.ts b/packages/effect/typetest/cli/Prompt.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cli/Prompt.tst.ts rename to packages/effect/typetest/cli/Prompt.tst.ts diff --git a/repos/effect/packages/effect/typetest/cluster/EntityProxyServer.tst.ts b/packages/effect/typetest/cluster/EntityProxyServer.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cluster/EntityProxyServer.tst.ts rename to packages/effect/typetest/cluster/EntityProxyServer.tst.ts diff --git a/repos/effect/packages/effect/typetest/cluster/K8sHttpClient.tst.ts b/packages/effect/typetest/cluster/K8sHttpClient.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cluster/K8sHttpClient.tst.ts rename to packages/effect/typetest/cluster/K8sHttpClient.tst.ts diff --git a/repos/effect/packages/effect/typetest/cluster/Reply.tst.ts b/packages/effect/typetest/cluster/Reply.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/cluster/Reply.tst.ts rename to packages/effect/typetest/cluster/Reply.tst.ts diff --git a/repos/effect/packages/effect/typetest/encoding/Ndjson.tst.ts b/packages/effect/typetest/encoding/Ndjson.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/encoding/Ndjson.tst.ts rename to packages/effect/typetest/encoding/Ndjson.tst.ts diff --git a/repos/effect/packages/effect/typetest/http/HttpClient.tst.ts b/packages/effect/typetest/http/HttpClient.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/http/HttpClient.tst.ts rename to packages/effect/typetest/http/HttpClient.tst.ts diff --git a/repos/effect/packages/effect/typetest/http/HttpPlatform.tst.ts b/packages/effect/typetest/http/HttpPlatform.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/http/HttpPlatform.tst.ts rename to packages/effect/typetest/http/HttpPlatform.tst.ts diff --git a/repos/effect/packages/effect/typetest/http/HttpRouter.tst.ts b/packages/effect/typetest/http/HttpRouter.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/http/HttpRouter.tst.ts rename to packages/effect/typetest/http/HttpRouter.tst.ts diff --git a/packages/effect/typetest/http/HttpServer.tst.ts b/packages/effect/typetest/http/HttpServer.tst.ts new file mode 100644 index 000000000..e32e079d7 --- /dev/null +++ b/packages/effect/typetest/http/HttpServer.tst.ts @@ -0,0 +1,26 @@ +import { Context, Effect, type Scope } from "effect" +import type { HttpServer } from "effect/http" +import { HttpServerRequest, HttpServerResponse } from "effect/http" +import { describe, expect, it } from "tstyche" + +class Config extends Context.Service()("Config") {} + +declare const server: HttpServer.HttpServer["Service"] + +describe("HttpServer", () => { + it("serve retains middleware services", () => { + const middleware = (app: Effect.Effect) => + Effect.flatMap(Config, () => app) + expect(server.serve(Effect.succeed(HttpServerResponse.empty()), middleware)).type.toBe< + Effect.Effect + >() + }) + + it("serve excludes services provided by middleware and the request", () => { + const app = Effect.flatMap(Config, () => Effect.as(HttpServerRequest.HttpServerRequest, HttpServerResponse.empty())) + const middleware = (app: Effect.Effect) => + Effect.provideService(app, Config, { enabled: true }) + + expect(server.serve(app, middleware)).type.toBe>() + }) +}) diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApi.tst.ts b/packages/effect/typetest/httpapi/HttpApi.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApi.tst.ts rename to packages/effect/typetest/httpapi/HttpApi.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiBuilder.tst.ts b/packages/effect/typetest/httpapi/HttpApiBuilder.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiBuilder.tst.ts rename to packages/effect/typetest/httpapi/HttpApiBuilder.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiClient.tst.ts b/packages/effect/typetest/httpapi/HttpApiClient.tst.ts similarity index 98% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiClient.tst.ts rename to packages/effect/typetest/httpapi/HttpApiClient.tst.ts index 707202bf7..76059e69f 100644 --- a/repos/effect/packages/effect/typetest/httpapi/HttpApiClient.tst.ts +++ b/packages/effect/typetest/httpapi/HttpApiClient.tst.ts @@ -595,6 +595,22 @@ describe("HttpApiClient", () => { expect(f({ responseMode: "response-only" })).type.toBe< Effect.Effect >() + + expect(f({ responseMode: hole<"decoded-only" | "response-only">() })).type.toBe< + Effect.Effect< + { readonly a: number } | HttpClientResponse.HttpClientResponse, + HttpClientError.HttpClientError | Schema.SchemaError + > + >() + + expect(f({ responseMode: hole() })).type.toBe< + Effect.Effect< + | { readonly a: number } + | HttpClientResponse.HttpClientResponse + | [{ readonly a: number }, HttpClientResponse.HttpClientResponse], + HttpClientError.HttpClientError | Schema.SchemaError + > + >() }) it("preserves method parameters across response modes", () => { diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiEndpoint.tst.ts b/packages/effect/typetest/httpapi/HttpApiEndpoint.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiEndpoint.tst.ts rename to packages/effect/typetest/httpapi/HttpApiEndpoint.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiGroup.tst.ts b/packages/effect/typetest/httpapi/HttpApiGroup.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiGroup.tst.ts rename to packages/effect/typetest/httpapi/HttpApiGroup.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiMiddleware.tst.ts b/packages/effect/typetest/httpapi/HttpApiMiddleware.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiMiddleware.tst.ts rename to packages/effect/typetest/httpapi/HttpApiMiddleware.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/HttpApiSchema.tst.ts b/packages/effect/typetest/httpapi/HttpApiSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/HttpApiSchema.tst.ts rename to packages/effect/typetest/httpapi/HttpApiSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/httpapi/OpenApiRepresentation.tst.ts b/packages/effect/typetest/httpapi/OpenApiRepresentation.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/httpapi/OpenApiRepresentation.tst.ts rename to packages/effect/typetest/httpapi/OpenApiRepresentation.tst.ts diff --git a/repos/effect/packages/effect/typetest/net/IpInterface.tst.ts b/packages/effect/typetest/net/IpInterface.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/net/IpInterface.tst.ts rename to packages/effect/typetest/net/IpInterface.tst.ts diff --git a/repos/effect/packages/effect/typetest/net/IpNetwork.tst.ts b/packages/effect/typetest/net/IpNetwork.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/net/IpNetwork.tst.ts rename to packages/effect/typetest/net/IpNetwork.tst.ts diff --git a/repos/effect/packages/effect/typetest/net/NetAddress.tst.ts b/packages/effect/typetest/net/NetAddress.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/net/NetAddress.tst.ts rename to packages/effect/typetest/net/NetAddress.tst.ts diff --git a/repos/effect/packages/effect/typetest/reactivity/AsyncResult.tst.ts b/packages/effect/typetest/reactivity/AsyncResult.tst.ts similarity index 89% rename from repos/effect/packages/effect/typetest/reactivity/AsyncResult.tst.ts rename to packages/effect/typetest/reactivity/AsyncResult.tst.ts index 6a838a9d0..07f4ad4fc 100644 --- a/repos/effect/packages/effect/typetest/reactivity/AsyncResult.tst.ts +++ b/packages/effect/typetest/reactivity/AsyncResult.tst.ts @@ -77,4 +77,12 @@ describe("AsyncResult", () => { expect(missingSuccess).type.not.toHaveProperty("exhaustive") }) }) + it("flatMap", () => { + expect( + AsyncResult.success(1).pipe(AsyncResult.flatMap((value) => AsyncResult.success(value))) + ).type.toBe>() + expect( + AsyncResult.success(1).pipe(AsyncResult.flatMap((value) => AsyncResult.success(String(value)))) + ).type.toBe>() + }) }) diff --git a/packages/effect/typetest/reactivity/Atom.tst.ts b/packages/effect/typetest/reactivity/Atom.tst.ts new file mode 100644 index 000000000..0950b56e7 --- /dev/null +++ b/packages/effect/typetest/reactivity/Atom.tst.ts @@ -0,0 +1,43 @@ +import { Effect, Layer, SubscriptionRef } from "effect" +import type { AsyncResult } from "effect/reactivity" +import { Atom } from "effect/reactivity" +import { describe, expect, it } from "tstyche" + +describe("Atom", () => { + describe("context", () => { + it("returns a registry runtime factory by default", () => { + expect(Atom.context()).type.toBe() + }) + + it("returns a registry runtime factory for an atom-backed memo map", () => { + const memoMap = Atom.make(() => Layer.makeMemoMapUnsafe()) + + expect(Atom.context({ memoMap })).type.toBe() + }) + + it("returns a shared runtime factory for a concrete memo map", () => { + const memoMap = Layer.makeMemoMapUnsafe() + + expect(Atom.context({ memoMap })).type.toBe() + }) + }) + + describe("AtomRuntime.subscriptionRef", () => { + it("includes the runtime error for an infallible ref", () => { + const runtime = Atom.runtime(Layer.effectDiscard(Effect.fail("setup-failed" as const))) + + expect(runtime.subscriptionRef(SubscriptionRef.make(1))) + .type.toBe, number>>() + }) + + it("preserves ref errors alongside runtime errors for both input forms", () => { + const runtime = Atom.runtime(Layer.effectDiscard(Effect.fail("setup-failed" as const))) + const ref: Effect.Effect, "ref-failed"> = Effect.fail("ref-failed") + + expect(runtime.subscriptionRef(ref)) + .type.toBe, number>>() + expect(runtime.subscriptionRef(() => ref)) + .type.toBe, number>>() + }) + }) +}) diff --git a/repos/effect/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts b/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts similarity index 93% rename from repos/effect/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts rename to packages/effect/typetest/reactivity/AtomHttpApi.tst.ts index e39cc0c4c..92a032ef5 100644 --- a/repos/effect/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts +++ b/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts @@ -1,4 +1,4 @@ -import { Effect, Layer, Schema, type Stream } from "effect" +import { Effect, hole, Layer, Schema, type Stream } from "effect" import type { Sse } from "effect/encoding" import { HttpClient, type HttpClientError, type HttpClientResponse } from "effect/http" import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema } from "effect/http-api" @@ -114,6 +114,13 @@ describe("AtomHttpApi", () => { >() }) + it("returns union successes for union response modes in mutations", () => { + const responseMode = hole<"decoded-only" | "response-only">() + const mutation = Client.mutation("group", "get", { responseMode }) + + expect>().type.toBe() + }) + it("should expose generated SSE stream errors from queries", () => { const query = Client.query("group", "sse", {}) diff --git a/repos/effect/packages/effect/typetest/reactivity/AtomRpc.tst.ts b/packages/effect/typetest/reactivity/AtomRpc.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/reactivity/AtomRpc.tst.ts rename to packages/effect/typetest/reactivity/AtomRpc.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/Rpc.tst.ts b/packages/effect/typetest/rpc/Rpc.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/Rpc.tst.ts rename to packages/effect/typetest/rpc/Rpc.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/RpcClient.tst.ts b/packages/effect/typetest/rpc/RpcClient.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/RpcClient.tst.ts rename to packages/effect/typetest/rpc/RpcClient.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/RpcGroup.tst.ts b/packages/effect/typetest/rpc/RpcGroup.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/RpcGroup.tst.ts rename to packages/effect/typetest/rpc/RpcGroup.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/RpcMessage.tst.ts b/packages/effect/typetest/rpc/RpcMessage.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/RpcMessage.tst.ts rename to packages/effect/typetest/rpc/RpcMessage.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/RpcMiddleware.tst.ts b/packages/effect/typetest/rpc/RpcMiddleware.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/RpcMiddleware.tst.ts rename to packages/effect/typetest/rpc/RpcMiddleware.tst.ts diff --git a/repos/effect/packages/effect/typetest/rpc/RpcServer.tst.ts b/packages/effect/typetest/rpc/RpcServer.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/rpc/RpcServer.tst.ts rename to packages/effect/typetest/rpc/RpcServer.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Array.tst.ts b/packages/effect/typetest/schema/Array.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Array.tst.ts rename to packages/effect/typetest/schema/Array.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/FromJsonSchema.tst.ts b/packages/effect/typetest/schema/FromJsonSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/FromJsonSchema.tst.ts rename to packages/effect/typetest/schema/FromJsonSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Graph.tst.ts b/packages/effect/typetest/schema/Graph.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Graph.tst.ts rename to packages/effect/typetest/schema/Graph.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Literals.tst.ts b/packages/effect/typetest/schema/Literals.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Literals.tst.ts rename to packages/effect/typetest/schema/Literals.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Record.tst.ts b/packages/effect/typetest/schema/Record.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Record.tst.ts rename to packages/effect/typetest/schema/Record.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Schema.tst.ts b/packages/effect/typetest/schema/Schema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Schema.tst.ts rename to packages/effect/typetest/schema/Schema.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaAOTCompiler.tst.ts b/packages/effect/typetest/schema/SchemaAOTCompiler.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaAOTCompiler.tst.ts rename to packages/effect/typetest/schema/SchemaAOTCompiler.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts b/packages/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts rename to packages/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts b/packages/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts rename to packages/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaCompiler.tst.ts b/packages/effect/typetest/schema/SchemaCompiler.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaCompiler.tst.ts rename to packages/effect/typetest/schema/SchemaCompiler.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaGetter.tst.ts b/packages/effect/typetest/schema/SchemaGetter.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaGetter.tst.ts rename to packages/effect/typetest/schema/SchemaGetter.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts b/packages/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts rename to packages/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaRepresentation.tst.ts b/packages/effect/typetest/schema/SchemaRepresentation.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaRepresentation.tst.ts rename to packages/effect/typetest/schema/SchemaRepresentation.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts b/packages/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts rename to packages/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/SchemaRepresentationReviver.tst.ts b/packages/effect/typetest/schema/SchemaRepresentationReviver.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/SchemaRepresentationReviver.tst.ts rename to packages/effect/typetest/schema/SchemaRepresentationReviver.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Struct.tst.ts b/packages/effect/typetest/schema/Struct.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Struct.tst.ts rename to packages/effect/typetest/schema/Struct.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Tuple.tst.ts b/packages/effect/typetest/schema/Tuple.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/Tuple.tst.ts rename to packages/effect/typetest/schema/Tuple.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/Union.tst.ts b/packages/effect/typetest/schema/Union.tst.ts similarity index 98% rename from repos/effect/packages/effect/typetest/schema/Union.tst.ts rename to packages/effect/typetest/schema/Union.tst.ts index f7b0db136..0902592ea 100644 --- a/repos/effect/packages/effect/typetest/schema/Union.tst.ts +++ b/packages/effect/typetest/schema/Union.tst.ts @@ -121,6 +121,7 @@ describe("Union", () => { const schema = original.pipe(Schema.toTaggedUnion("_tag")) + expect(schema.tag).type.toBe<"_tag">() expect(schema.discriminants).type.toBe() expect(Schema.revealCodec(schema)).type.toBe< @@ -153,6 +154,8 @@ describe("Union", () => { Schema.Struct({ kind: Schema.tag("c"), c: Schema.Boolean }) ]).pipe(Schema.toTaggedUnion("kind")) + expect(schema.tag).type.toBe<"kind">() + const value = hole>() if (schema.isAnyOf(["a", "b"])(value)) { @@ -265,6 +268,8 @@ describe("Union", () => { B: { b: Schema.FiniteFromString } }).annotate({}) + expect(schema.tag).type.toBe<"_tag">() + expect(Schema.revealCodec(schema)).type.toBe< Schema.Codec< | { readonly _tag: "A"; readonly a: string } diff --git a/repos/effect/packages/effect/typetest/schema/api.tst.ts b/packages/effect/typetest/schema/api.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/api.tst.ts rename to packages/effect/typetest/schema/api.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/options.tst.ts b/packages/effect/typetest/schema/options.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/options.tst.ts rename to packages/effect/typetest/schema/options.tst.ts diff --git a/repos/effect/packages/effect/typetest/schema/toIso.tst.ts b/packages/effect/typetest/schema/toIso.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/schema/toIso.tst.ts rename to packages/effect/typetest/schema/toIso.tst.ts diff --git a/repos/effect/packages/effect/typetest/socket/Socket.tst.ts b/packages/effect/typetest/socket/Socket.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/socket/Socket.tst.ts rename to packages/effect/typetest/socket/Socket.tst.ts diff --git a/repos/effect/packages/effect/typetest/sql/SqlSchema.tst.ts b/packages/effect/typetest/sql/SqlSchema.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/sql/SqlSchema.tst.ts rename to packages/effect/typetest/sql/SqlSchema.tst.ts diff --git a/repos/effect/packages/effect/typetest/workflow/Workflow.tst.ts b/packages/effect/typetest/workflow/Workflow.tst.ts similarity index 100% rename from repos/effect/packages/effect/typetest/workflow/Workflow.tst.ts rename to packages/effect/typetest/workflow/Workflow.tst.ts diff --git a/packages/eslint-codegen-model/CHANGELOG.md b/packages/eslint-codegen-model/CHANGELOG.md deleted file mode 100644 index cdc729531..000000000 --- a/packages/eslint-codegen-model/CHANGELOG.md +++ /dev/null @@ -1,1354 +0,0 @@ -# @effect-app/eslint-codegen-model - -## 2.0.0-beta.27 - -### Patch Changes - -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. - -## 2.0.0-beta.26 - -### Patch Changes - -- e71b67a: run codegen model from src locally - -## 2.0.0-beta.25 - -### Minor Changes - -- 82655fa: feat: add native fork - -### Patch Changes - -- 21ac90a: Unify generated model facades on `OpaqueFacade` and support class facades through encoded-key schema transforms. -- d605f3d: Migrate `glob` 8 → 11. Use the named `globSync` export (glob 11 dropped the default export, which broke loading the package as an ESM oxlint plugin), and drop the stale `@types/glob` (glob 11 ships its own types). Also clears the package's pre-existing implicit-`any` typecheck errors. - -## 2.0.0-beta.24 - -### Patch Changes - -- 50b022e: Harden model facades and add `OpaqueErrorFacadeClass`. - - - `OpaqueErrorFacadeClass`: facade `TaggedErrorClass`/`ErrorClass` while keeping - `Cause.YieldableError` on the constructed instance (so `yield* new Err()`, - `Effect.fail`, and `instanceof` keep working through the facade). - - `OpaqueFacadeInput` relaxed to require only the codec service channels, so - transformed schemas (`.pipe(encodeKeys/annotate/filter/...)`) can be facaded; - `fields`/`copy`/`mapFields` flow through `OpaqueFacadeStatics` when present, and - `to` is carried so models that compose via `X.to.fields` keep working. - - Dropped the wide `fields`/`mapFields` overrides on the facade interfaces so the - precise statics win (keeps `Q.project(X.mapFields(...))` typed). - - Codegen (`eslint-codegen-model`): resolver prefers the private `_X` over the - self-referential facade; converges static service types in one run; per-model - classification (facade Opaque models, leave `Class` standard in mixed files); - `Make` emitted as `type X = {...} | void` when the make-input is voidable; - `readonly`-prefixed array/tuple elements parenthesized; value self-references in - the moved `_X` body rewritten to `_X`; instance getters surface on `Self`. - -- 50b022e: Add generated opaque model facades that expose static Type, Encoded, Make, and Schema declarations without leaking the private struct schema type to downstream project references. - -## 2.0.0-beta.23 - -### Patch Changes - -- 55c6572: update packages - -## 2.0.0-beta.22 - -### Patch Changes - -- b2e0f62: Allow oxlint codegen rule options so shared preset defaults can be configured. - -## 2.0.0-beta.21 - -### Patch Changes - -- 22be885: Add `stripSuffixes` option to the meta codegen preset and configure `.Queries` stripping in the shared oxlint config. - -## 2.0.0-beta.20 - -### Patch Changes - -- bogus - -## 2.0.0-beta.19 - -### Patch Changes - -- b8b9080: update packages - -## 2.0.0-beta.18 - -### Minor Changes - -- 34ab818: Add `importExtension` option to the `barrel` preset to control the file extension emitted on generated imports/exports (defaults to `.js`). Configurable per block, globally via the oxlint rule option `["error", { barrel: { importExtension: ".ts" } }]`, or via `codegen.config.json` (CLI also accepts `--config `). Resolution order: preset default → global defaults → per-block options. - -### Patch Changes - -- ccd4845: set default barrel ext to .ts - -## 2.0.0-beta.17 - -### Patch Changes - -- f04327a: Fix `toCamelCase`/`toPascalCase` in barrel preset to preserve case boundaries. Mixed-case input like `AnCamelCase` was collapsed to `Ancamelcase` because the word splitter only matched contiguous alphanumerics. Now splits on case transitions (matching prior `lodash.camelCase` + `startCase` behavior). - -## 2.0.0-beta.15 - -### Patch Changes - -- c991be1: update packages - -## 2.0.0-beta.14 - -### Patch Changes - -- 830e1ce: Optimize codegen barrel and model plugins: - - Drop `io-ts-extra`, `io-ts`, and `lodash` dependencies; replace with native JS helpers - - Add fast string comparison (skip AST parse) for barrel and model equality checks - - Avoid triple file reads in the CLI path: pass already-read source into `model` via context - - Eliminate double read in `run()`: pass source string into `updateFile` - - Extract shared logic (`blockRe`, helpers, `renderPreset`) into `src/shared/codegen-block.ts` - - Remove dead `fs.existsSync`/`fs.statSync` checks in `model` preset - - Use `Set` for O(1) dedup in `model` preset - - Move `last` helper to module scope in `barrel` preset - - Pre-compile all regex constants at module scope; use fresh `RegExp` copy per oxlint `Program` visit to avoid shared `lastIndex` mutations - -## 2.0.0-beta.13 - -### Patch Changes - -- 8c753cb: align with earlier updates - -## 2.0.0-beta.12 - -### Patch Changes - -- 50ce7e6: Cleanup after tsgolint + oxlint-codegen-plugin migration: - - Wire `@effect-app/eslint-codegen-model/oxlint` via `jsPlugins` object form (`{ name: "codegen", specifier: ... }`) so the `codegen/codegen` rule key resolves. - - Drop `eslint-plugin-codegen` dep, patch, and `augmentedConfig` helper — codegen now runs through oxlint. - - Break cyclic workspace dep between `eslint-codegen-model` and `eslint-shared-config`; remove dead `eslint.config.mjs` from `eslint-codegen-model`. - - Switch `@effect-app/vue` to oxlint-only (no `.vue` files in `src`); drop its ESLint config and `eslint-shared-config` devDep. - - Restore `@typescript-eslint` plugin and rules in shared `baseConfig` so inline `eslint-disable @typescript-eslint/...` directives resolve in `@effect-app/vue-components` (the only remaining ESLint consumer, for `.vue` files). - - Add `globals.browser` to `vueConfig` so browser globals (`window`, `console`, `URL`, etc.) resolve. - -## 2.0.0-beta.11 - -### Patch Changes - -- c1e73de: - -## 2.0.0-beta.10 - -### Patch Changes - -- 6fff09c: unify encoded function for when you use encodedKeys - -## 2.0.0-beta.9 - -### Patch Changes - -- 25008fb: Ignore commented-out exported classes when detecting model schemas for generated `Encoded` namespaces. -- 77efa95: Detect `Schema.Opaque()` model classes in the model preset while excluding `Context.Opaque` service tags. - -## 2.0.0-beta.8 - -### Patch Changes - -- 025de47: Fold the encoded-override support from `ExtendedClass` and `ExtendedTaggedClass` into `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass`, and update model codegen to detect the new second-generic form. - -## 2.0.0-beta.7 - -### Patch Changes - -- c3299f7: update packages - -## 2.0.0-beta.6 - -### Patch Changes - -- 3b27909: Don't export `Req` in meta codegen preset - -## 2.0.0-beta.5 - -### Minor Changes - -- 3613e87: Add `TaggedRequestFor` helper to `makeRpcClient` that curries a `moduleName`, producing request classes with static `id` and `moduleName` properties. This enables passing request classes directly to `makeQueryKey` without going through `clientFor` first. The `clientFor` function no longer requires a `meta` property on the module when requests carry `moduleName`. The meta codegen preset now generates `Req = TaggedRequestFor(moduleName)`. Original `TaggedRequest` remains for backwards compatibility. - -## 2.0.0-beta.4 - -### Patch Changes - -- cec026d: update packages - -## 2.0.0-beta.3 - -### Patch Changes - -- 10b55ff: update packages - -## 2.0.0-beta.2 - -### Patch Changes - -- 01c70d0: update all teh tings - -## 2.0.0-beta.1 - -### Patch Changes - -- 64786af: Beta25 - -## 2.0.0-beta.0 - -### Major Changes - -- 880df28: Effect v4 beta - -## 1.47.0 - -### Minor Changes - -- 1a102e2: update packages - -## 1.46.1 - -### Patch Changes - -- 358782e: update packages - -## 1.46.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -## 1.45.0 - -### Minor Changes - -- b8f5e30: update packages - -## 1.44.0 - -### Minor Changes - -- 6d8c512: update packages - -## 1.43.0 - -### Minor Changes - -- 3d49d81: update packages - -## 1.42.3 - -### Patch Changes - -- 8096576: new eslint shared config package - -## 1.42.2 - -### Patch Changes - -- f6a3328: export base eslint files so that we import them in projects - -## 1.42.1 - -### Patch Changes - -- 8cb893c: dprint rules the world - -## 1.42.0 - -### Minor Changes - -- 0f2b2cf: update packages - -## 1.41.0 - -### Minor Changes - -- 0a82e7f: update packages - -## 1.40.1 - -### Patch Changes - -- b095bb5: tscp - -## 1.40.0 - -### Minor Changes - -- 2c217ce: update packages - -## 1.39.1 - -### Patch Changes - -- b771967: update packages - -## 1.39.0 - -### Minor Changes - -- eac9c67: update packages - -## 1.38.1 - -### Patch Changes - -- 49d93a5: update packages - -## 1.38.0 - -### Minor Changes - -- b1d4af4: housekeeping: update packages - -## 1.37.0 - -### Minor Changes - -- 8cb33d5: update packages - -## 1.36.4 - -### Patch Changes - -- 1e1f32b: housekeeping: update packages - -## 1.36.3 - -### Patch Changes - -- 8b7fdbe: update packages - -## 1.36.2 - -### Patch Changes - -- update packages - -## 1.36.1 - -### Patch Changes - -- 0fcab38: update packages - -## 1.36.0 - -### Minor Changes - -- 14ea804: housekeeping: update packages - -## 1.35.1 - -### Patch Changes - -- 4c8dfb4: update packages - -## 1.35.0 - -### Minor Changes - -- e42d5fb: update to effect 3.14 with native rpc2 - -## 1.34.3 - -### Patch Changes - -- 771ac45: update packages - -## 1.34.2 - -### Patch Changes - -- 36996ed: housekeeping: update packages - -## 1.34.1 - -### Patch Changes - -- 63e966a: update packages - -## 1.34.0 - -### Minor Changes - -- 8be2984: housekeeping: update packages - -## 1.33.0 - -### Minor Changes - -- 9b2e385: update packages - -## 1.32.0 - -### Minor Changes - -- 2d0ad81: update packages - -## 1.31.2 - -### Patch Changes - -- 9d11dfb: housekeeping: update packages - -## 1.31.1 - -### Patch Changes - -- ab9d1ea: housekeeping: update packages - -## 1.31.0 - -### Minor Changes - -- c8ebfb4: update packages - -## 1.30.4 - -### Patch Changes - -- 9f504c6: update packages - -## 1.30.3 - -### Patch Changes - -- 750b46a: update packages - -## 1.30.2 - -### Patch Changes - -- 1bfc3d8: update packages - -## 1.30.1 - -### Patch Changes - -- 5ef6481: update packages - -## 1.30.0 - -### Minor Changes - -- 29a1a65: update packages - -## 1.29.0 - -### Minor Changes - -- d84aef7: update packages - -## 1.28.0 - -### Minor Changes - -- cc2c43e: update packages - -## 1.27.2 - -### Patch Changes - -- 125e2ea: update packages - -## 1.27.1 - -### Patch Changes - -- 0feb32c: update packages - -## 1.27.0 - -### Minor Changes - -- bump - -## 1.26.1 - -### Patch Changes - -- c971d73: update packages - -## 1.26.0 - -### Minor Changes - -- update - -## 1.25.0 - -### Minor Changes - -- f19762b: update packages - -## 1.24.1 - -### Patch Changes - -- 412cb3d: update packages - -## 1.24.0 - -### Minor Changes - -- 58d373f: switch from `From` to `Encoded` - -## 1.23.0 - -### Minor Changes - -- 590e5bf: update packages - -## 1.22.0 - -### Minor Changes - -- d4ab73f: update packages - -## 1.21.0 - -### Minor Changes - -- d5623a6: update packages - -## 1.20.1 - -### Patch Changes - -- f68dbd2: update packages - -## 1.20.0 - -### Minor Changes - -- 2dfcb9f: remove "resources" level from moduleName - -## 1.19.0 - -### Minor Changes - -- 3872061: migrate to effect/Schema - -## 1.18.1 - -### Patch Changes - -- cda1477: update packages - -## 1.18.0 - -### Minor Changes - -- 3d37a85: improve: moduleName as const - -### Patch Changes - -- 3d37a85: use as const - -## 1.17.0 - -### Minor Changes - -- update packages - -## 1.16.0 - -### Minor Changes - -- 9ddb31b: update packages - -## 1.15.0 - -### Minor Changes - -- 0effd97: update packages - -## 1.14.0 - -### Minor Changes - -- b9b64b8: housekeeping: update packages - -## 1.13.0 - -### Minor Changes - -- 65dcc73: update packages - -## 1.12.0 - -### Minor Changes - -- 4d2b804: housekeeping: update packages - -## 1.11.0 - -### Minor Changes - -- 1018e58: update packages - -## 1.10.0 - -### Minor Changes - -- b3d9a3e: update packages - -## 1.9.1 - -### Patch Changes - -- d08286c: update packages - -## 1.9.0 - -### Minor Changes - -- c5f5223: update packages - -## 1.8.3 - -### Patch Changes - -- d61e00a: fix rule - -## 1.8.2 - -### Patch Changes - -- f45f63d: fix typo - -## 1.8.1 - -### Patch Changes - -- update packages - -## 1.8.0 - -### Minor Changes - -- 08361d8: modulegen hack - -## 1.7.4 - -### Patch Changes - -- e6eecd2: update packages - -## 1.7.3 - -### Patch Changes - -- ff - -## 1.7.2 - -### Patch Changes - -- fix - -## 1.7.1 - -### Patch Changes - -- 0b399ce: update packages - -## 1.7.0 - -### Minor Changes - -- 2143dd9: update packages - -## 1.6.1 - -### Patch Changes - -- e120966: update packages -- f6066ac: update packages - -## 1.6.0 - -### Minor Changes - -- 79f58af: update packages - -## 1.5.2 - -### Patch Changes - -- b3ebc41: update packages - -## 1.5.1 - -### Patch Changes - -- f569681: update packages - -## 1.5.0 - -### Minor Changes - -- 613c148: update packages - -## 1.4.0 - -### Minor Changes - -- 8d0c511: update packages - -## 1.3.0 - -### Minor Changes - -- 9cc6f86: update packages - -## 1.2.3 - -### Patch Changes - -- 349c421: Update packages - -## 1.2.2 - -### Patch Changes - -- 9d944a1: update packages - -## 1.2.1 - -### Patch Changes - -- 2c661c1: update packages - -## 1.2.0 - -### Minor Changes - -- 9ac9416: update packages - -## 1.1.2 - -### Patch Changes - -- 3411e78: update packages - -## 1.1.1 - -### Patch Changes - -- 128f636: update packages - -## 1.1.0 - -### Minor Changes - -- 2e29520: update packages - -## 1.0.4 - -### Patch Changes - -- c99cdbb: update packages - -## 1.0.3 - -### Patch Changes - -- c4dbb6c: update packages - -## 1.0.2 - -### Patch Changes - -- caa40e4: update packagesy - -## 1.0.1 - -### Patch Changes - -- bf702ba: update packages - -## 1.0.0 - -### Major Changes - -- 4e9992f: effect 3.0! - -## 0.75.0 - -### Minor Changes - -- 0d1b378: update to latest schema - -## 0.74.0 - -### Minor Changes - -- 290a709: update packages - -## 0.73.0 - -### Minor Changes - -- ecb05f6: update packages - -## 0.72.0 - -### Minor Changes - -- 01f8283: cleanup: drop legacy `FromClass` - -## 0.71.0 - -### Minor Changes - -- revert form changes - -## 0.70.0 - -### Minor Changes - -- 7a3f05c: update packages - -## 0.69.0 - -### Minor Changes - -- bbaa558: bs - -## 0.68.0 - -### Minor Changes - -- e5b0d8e: update packages -- 2e2fa98: update packages - -## 0.67.0 - -### Minor Changes - -- 494842c: update packages - -## 0.66.0 - -### Minor Changes - -- 498deb0: update packages - -## 0.65.0 - -### Minor Changes - -- fd8f019: update packages - -## 0.64.0 - -### Minor Changes - -- 51253a2: update packages - -## 0.63.0 - -### Minor Changes - -- f2c3553: update packages - -## 0.62.0 - -### Minor Changes - -- update packages - -## 0.61.0 - -### Minor Changes - -- 5178025: update packages - -## 0.60.1 - -### Patch Changes - -- 326c1ce: fix model gen - -## 0.60.0 - -### Minor Changes - -- d0a1774: update ts to 5.4.1-rc - -## 0.59.2 - -### Patch Changes - -- 2fd7c69: fix for comments - -## 0.59.1 - -### Patch Changes - -- 5243e13: remove need for ts compiler - -## 0.59.0 - -### Minor Changes - -- 7346691: update packages - -## 0.58.0 - -### Minor Changes - -- e8e8ee2: update packages - -## 0.57.1 - -### Patch Changes - -- 0a312c3: update packages - -## 0.57.0 - -### Minor Changes - -- 117be64: bump - -## 0.56.0 - -### Minor Changes - -- A new day -- 78b3c9e: reduce tspuse - -## 0.55.0 - -### Minor Changes - -- c070e0d: update effect - -## 0.54.0 - -### Minor Changes - -- 57fcfef: update packages - -## 0.53.1 - -### Patch Changes - -- d8496db: peer - -## 0.53.0 - -### Minor Changes - -- d478694: handle double namespace as one - -## 0.52.0 - -### Minor Changes - -- 29c5f16: detsp - -## 0.51.0 - -### Minor Changes - -- 6e5c606: cl - -## 0.50.0 - -### Minor Changes - -- de336f6: src - -## 0.49.1 - -### Patch Changes - -- d7ebf34: enable sourcePrefix option - -## 0.49.0 - -### Minor Changes - -- m - -## 0.48.0 - -### Minor Changes - -- bs - -## 0.47.0 - -### Minor Changes - -- 17cfe0f: update packages - -## 0.46.0 - -### Minor Changes - -- c8a07bc: fix path - -## 0.45.1 - -### Patch Changes - -- 200a840: address - -## 0.45.0 - -### Minor Changes - -- c29b6ff: bump - -## 0.44.0 - -### Minor Changes - -- bump - -## 0.43.0 - -### Minor Changes - -- 034717f: update packages - -## 0.42.0 - -### Minor Changes - -- fa729da: detect instead the Extended Class, instead of the decorator - -## 0.41.0 - -### Minor Changes - -- 99f79ab: update packages - -## 0.40.1 - -### Patch Changes - -- dd60aca: fix - -## 0.40.0 - -### Minor Changes - -- a5c50bb: feat: add meta codegen - -## 0.39.0 - -### Minor Changes - -- 4c915ea: update packages - -## 0.38.0 - -### Minor Changes - -- 33dcf2e: update packages - -## 0.37.0 - -### Minor Changes - -- 63425a7: update packages - -## 0.36.1 - -### Patch Changes - -- c615f35: update packages - -## 0.36.0 - -### Minor Changes - -- 640d24a: update packages - -### Patch Changes - -- 640d24a: update package -- 640d24a: codegen update -- 640d24a: update packages - -## 0.36.0-next.3 - -### Patch Changes - -- 2b721f7: update packages - -## 0.36.0-next.2 - -### Patch Changes - -- 2597bfd: codegen update - -## 0.36.0-next.1 - -### Patch Changes - -- e7acd67: update package - -## 0.36.0-next.0 - -### Minor Changes - -- 87a6ac2: update packages - -## 0.35.1 - -### Patch Changes - -- 350f685: update packages - -## 0.35.0 - -### Minor Changes - -- 67eb741: update packages - -## 0.34.0 - -### Minor Changes - -- fb332b4: update packages - -## 0.33.0 - -### Minor Changes - -- a9e8b8f: update packages - -## 0.32.0 - -### Minor Changes - -- align v - -## 0.31.0 - -### Minor Changes - -- align v - -## 0.30.0 - -### Minor Changes - -- c528c33: initial alignment of Schema with effect/schema - -## 0.29.1 - -### Patch Changes - -- ab7eddc: update packages - -## 0.29.0 - -### Minor Changes - -- 6c180253: cleanup - -## 0.28.0 - -### Minor Changes - -- 0a4bfbc7: update packages - -## 0.27.0 - -### Minor Changes - -- 40a6c3b6: update packages - -## 0.26.2 - -### Patch Changes - -- 263a56b9: update packages - -## 0.26.1 - -### Patch Changes - -- aa3c98d1: update packages - -## 0.26.0 - -### Minor Changes - -- update packages - -## 0.25.3 - -### Patch Changes - -- 780a6718: update packages - -## 0.25.2 - -### Patch Changes - -- 838eeb10: update packages - -## 0.25.1 - -### Patch Changes - -- fix tsplus.config for pnpm - -## 0.25.0 - -### Minor Changes - -- update packages - -## 0.24.0 - -### Minor Changes - -- add match and update packages - -## 0.23.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.22.0 - -### Minor Changes - -- Update packages - -## 0.21.0 - -### Minor Changes - -- Improve codegen - -## 0.20.0 - -### Minor Changes - -- update packages and cleanup - -## 0.19.0 - -### Minor Changes - -- update dependencies - -## 0.18.0 - -### Minor Changes - -- update packages - -## 0.17.0 - -### Minor Changes - -- Update to latest effect - -## 0.16.0 - -### Minor Changes - -- update packages - -## 0.15.1 - -### Patch Changes - -- fix glob - -## 0.15.0 - -### Minor Changes - -- fix double quotes - -## 0.14.0 - -### Minor Changes - -- Improve Encoded codegen - -## 0.13.0 - -### Minor Changes - -- Update tsplus with companion instance extensions - -## 0.12.2 - -### Patch Changes - -- 28d5530b: Fix Encoded - -## 0.12.1 - -### Patch Changes - -- 315487e5: Fix unneeded interface - -## 0.12.0 - -### Minor Changes - -- 0ed16973: Disable full-type generation by default - -## 0.11.0 - -### Minor Changes - -- Update annotations, drop Aspects for now - -## 0.10.0 - -### Minor Changes - -- 4d49f303: Upgrade to @effect/data - -## 0.9.1 - -### Patch Changes - -- 912ba66c: Update packages - -## 0.9.0 - -### Minor Changes - -- 67716050: Update to TS5 beta - -## 0.8.2 - -### Patch Changes - -- Disable trace - -## 0.8.1 - -### Patch Changes - -- Use ReadonlyMap - -## 0.8.0 - -### Minor Changes - -- bba14672: Update to latest @effect/io and @effect/data diff --git a/packages/eslint-codegen-model/examples/model-facades.ts b/packages/eslint-codegen-model/examples/model-facades.ts deleted file mode 100644 index 2d2677f36..000000000 --- a/packages/eslint-codegen-model/examples/model-facades.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { Effect } from "effect" -import * as S from "../../effect-app/src/Schema.ts" - -export interface CatalogDecodingServices { - readonly CatalogDecodingServices: "CatalogDecodingServices" -} - -export interface CatalogEncodingServices { - readonly CatalogEncodingServices: "CatalogEncodingServices" -} - -const ServiceAwareSku = S.NonEmptyString255.pipe( - S.middlewareDecoding((effect) => Effect.context().pipe(Effect.flatMap(() => effect))), - S.middlewareEncoding((effect) => Effect.context().pipe(Effect.flatMap(() => effect))) -) - -class _CatalogItem extends S.Opaque<_CatalogItem>()( - S.Struct({ - id: S.StringId, - sku: ServiceAwareSku, - name: S.NonEmptyString255, - dimensions: S.Struct({ - width: S.Number, - height: S.Number, - depth: S.Number, - unit: S.Literals(["cm", "in"]) - }), - tags: S.Array(S.NonEmptyString255), - attributes: S.Record(S.String, S.Union([S.String, S.Number, S.Boolean])), - discontinuedAt: S.NullOr(S.DateFromString) - }) -) {} - -// codegen:start {preset: modelFacade, className: _CatalogItem, schema: S} -// eslint-disable-next-line typescript/no-unsafe-declaration-merging -export class CatalogItem extends S.OpaqueFacade< - CatalogItem, - CatalogItem.Encoded, - CatalogItem.Make, - CatalogItem.DecodingServices, - CatalogItem.EncodingServices ->()(_CatalogItem) {} -// codegen:end - -class _OrderLine extends S.Opaque<_OrderLine>()( - S.Struct({ - item: CatalogItem, - quantity: S.Int, - unitPrice: S.Struct({ - amount: S.Number, - currency: S.Literals(["EUR", "USD", "GBP"]) - }), - adjustments: S.Array( - S.Struct({ - code: S.String, - amount: S.Number, - reason: S.optional(S.String) - }) - ) - }) -) { - static defaultCurrency(): "EUR" { - return "EUR" - } -} - -// codegen:start {preset: modelFacade, className: _OrderLine, schema: S} -// eslint-disable-next-line typescript/no-unsafe-declaration-merging -export class OrderLine extends S.OpaqueFacade< - OrderLine, - OrderLine.Encoded, - OrderLine.Make, - OrderLine.DecodingServices, - OrderLine.EncodingServices ->()(_OrderLine) {} -// codegen:end - -export const defaultOrderCurrency: "EUR" = OrderLine.defaultCurrency() - -class _OrderAggregate extends S.Opaque<_OrderAggregate>()( - S.Struct({ - id: S.StringId, - customer: S.Struct({ - id: S.StringId, - email: S.NonEmptyString255, - profile: S.Struct({ - displayName: S.NonEmptyString255, - phone: S.NullOr(S.String), - address: S.Struct({ - line1: S.NonEmptyString255, - line2: S.optional(S.NonEmptyString255), - postalCode: S.NonEmptyString255, - city: S.NonEmptyString255, - country: S.NonEmptyString255 - }) - }) - }), - lines: S.NonEmptyArray(OrderLine), - fulfillment: S.Union([ - S.TaggedStruct("Pending", { - requestedAt: S.DateFromString - }), - S.TaggedStruct("Packed", { - packedAt: S.DateFromString, - packageIds: S.NonEmptyArray(S.StringId) - }), - S.TaggedStruct("Shipped", { - shippedAt: S.DateFromString, - carrier: S.NonEmptyString255, - trackingNumber: S.NonEmptyString255 - }) - ]), - audit: S.Array( - S.Struct({ - at: S.DateFromString, - actorId: S.StringId, - message: S.String, - metadata: S.Record(S.String, S.String) - }) - ) - }) -) {} - -// codegen:start {preset: modelFacade, className: _OrderAggregate, schema: S} -// eslint-disable-next-line typescript/no-unsafe-declaration-merging -export class OrderAggregate extends S.OpaqueFacade< - OrderAggregate, - OrderAggregate.Encoded, - OrderAggregate.Make, - OrderAggregate.DecodingServices, - OrderAggregate.EncodingServices ->()(_OrderAggregate) {} -// codegen:end - -// codegen:start {preset: model, static: true, facade: true} -// -export interface CatalogItem { - readonly id: S.StringId - readonly sku: S.NonEmptyString255 - readonly name: S.NonEmptyString255 - readonly dimensions: { - readonly width: number - readonly height: number - readonly depth: number - readonly unit: "cm" | "in" - } - readonly tags: readonly S.NonEmptyString255[] - readonly attributes: { readonly [x: string]: string | number | boolean } - readonly discontinuedAt: null | Date -} -export namespace CatalogItem { - export interface Encoded { - readonly id: string - readonly sku: string - readonly name: string - readonly dimensions: { - readonly width: number - readonly height: number - readonly depth: number - readonly unit: "cm" | "in" - } - readonly tags: readonly string[] - readonly attributes: { readonly [x: string]: string | number | boolean } - readonly discontinuedAt: null | string - } - export interface Make { - readonly id: S.StringId - readonly sku: S.NonEmptyString255 - readonly name: S.NonEmptyString255 - readonly dimensions: { - readonly width: number - readonly height: number - readonly depth: number - readonly unit: "cm" | "in" - } - readonly tags: readonly S.NonEmptyString255[] - readonly attributes: { readonly [x: string]: string | number | boolean } - readonly discontinuedAt: null | Date - } - export type DecodingServices = CatalogDecodingServices - export type EncodingServices = CatalogEncodingServices -} -export interface OrderLine { - readonly item: CatalogItem - readonly quantity: S.Int - readonly unitPrice: { readonly amount: number; readonly currency: "EUR" | "USD" | "GBP" } - readonly adjustments: - readonly ({ readonly code: string; readonly amount: number; readonly reason?: undefined | string })[] -} -export namespace OrderLine { - export interface Encoded { - readonly item: CatalogItem.Encoded - readonly quantity: number - readonly unitPrice: { readonly amount: number; readonly currency: "EUR" | "USD" | "GBP" } - readonly adjustments: - readonly ({ readonly code: string; readonly amount: number; readonly reason?: undefined | string })[] - } - export interface Make { - readonly item: CatalogItem.Make - readonly quantity: S.Int - readonly unitPrice: { readonly amount: number; readonly currency: "EUR" | "USD" | "GBP" } - readonly adjustments: - readonly ({ readonly code: string; readonly amount: number; readonly reason?: undefined | string })[] - } - export type DecodingServices = CatalogDecodingServices - export type EncodingServices = CatalogEncodingServices -} -export interface OrderAggregate { - readonly id: S.StringId - readonly customer: { - readonly id: S.StringId - readonly email: S.NonEmptyString255 - readonly profile: { - readonly displayName: S.NonEmptyString255 - readonly phone: null | string - readonly address: { - readonly line1: S.NonEmptyString255 - readonly postalCode: S.NonEmptyString255 - readonly city: S.NonEmptyString255 - readonly country: S.NonEmptyString255 - readonly line2?: undefined | S.NonEmptyString255 - } - } - } - readonly lines: readonly [OrderLine, ...OrderLine[]] - readonly fulfillment: { readonly _tag: "Pending"; readonly requestedAt: Date } | { - readonly _tag: "Packed" - readonly packedAt: Date - readonly packageIds: readonly [S.StringId, ...S.StringId[]] - } | { - readonly _tag: "Shipped" - readonly shippedAt: Date - readonly carrier: S.NonEmptyString255 - readonly trackingNumber: S.NonEmptyString255 - } - readonly audit: readonly { - readonly at: Date - readonly actorId: S.StringId - readonly message: string - readonly metadata: { readonly [x: string]: string } - }[] -} -export namespace OrderAggregate { - export interface Encoded { - readonly id: string - readonly customer: { - readonly id: string - readonly email: string - readonly profile: { - readonly displayName: string - readonly phone: null | string - readonly address: { - readonly line1: string - readonly postalCode: string - readonly city: string - readonly country: string - readonly line2?: undefined | string - } - } - } - readonly lines: readonly [OrderLine.Encoded, ...OrderLine.Encoded[]] - readonly fulfillment: { readonly _tag: "Pending"; readonly requestedAt: string } | { - readonly _tag: "Packed" - readonly packedAt: string - readonly packageIds: readonly [string, ...string[]] - } | { - readonly _tag: "Shipped" - readonly shippedAt: string - readonly carrier: string - readonly trackingNumber: string - } - readonly audit: readonly { - readonly at: string - readonly actorId: string - readonly message: string - readonly metadata: { readonly [x: string]: string } - }[] - } - export interface Make { - readonly id: S.StringId - readonly customer: { - readonly id: S.StringId - readonly email: S.NonEmptyString255 - readonly profile: { - readonly displayName: S.NonEmptyString255 - readonly phone: null | string - readonly address: { - readonly line1: S.NonEmptyString255 - readonly postalCode: S.NonEmptyString255 - readonly city: S.NonEmptyString255 - readonly country: S.NonEmptyString255 - readonly line2?: undefined | S.NonEmptyString255 - } - } - } - readonly lines: readonly [OrderLine.Make, ...OrderLine.Make[]] - readonly fulfillment: { readonly _tag: "Pending"; readonly requestedAt: Date } | { - readonly _tag: "Packed" - readonly packedAt: Date - readonly packageIds: readonly [S.StringId, ...S.StringId[]] - } | { - readonly _tag: "Shipped" - readonly shippedAt: Date - readonly carrier: S.NonEmptyString255 - readonly trackingNumber: S.NonEmptyString255 - } - readonly audit: readonly { - readonly at: Date - readonly actorId: S.StringId - readonly message: string - readonly metadata: { readonly [x: string]: string } - }[] - } - export type DecodingServices = CatalogDecodingServices - export type EncodingServices = CatalogEncodingServices -} -// -// codegen:end diff --git a/packages/eslint-codegen-model/examples/workflow-facades.ts b/packages/eslint-codegen-model/examples/workflow-facades.ts deleted file mode 100644 index 1a262a6d0..000000000 --- a/packages/eslint-codegen-model/examples/workflow-facades.ts +++ /dev/null @@ -1,184 +0,0 @@ -import * as S from "../../effect-app/src/Schema.ts" - -class _WorkflowStep extends S.Opaque<_WorkflowStep>()( - S.Struct({ - id: S.StringId, - name: S.NonEmptyString255, - timeout: S.optional(S.Number), - retryPolicy: S.Struct({ - maxAttempts: S.Int, - backoff: S.Literals(["fixed", "exponential"]), - jitter: S.Boolean - }), - input: S.Record(S.String, S.Union([S.String, S.Number, S.Boolean, S.Null])) - }) -) {} - -// codegen:start {preset: modelFacade, className: _WorkflowStep, schema: S} -export class WorkflowStep - extends S.OpaqueFacade< - WorkflowStep, - WorkflowStep.Encoded, - WorkflowStep.Make, - WorkflowStep.DecodingServices, - WorkflowStep.EncodingServices - >()(_WorkflowStep) -{} -// codegen:end - -class _WorkflowRun extends S.Opaque<_WorkflowRun>()( - S.Struct({ - id: S.StringId, - workflowName: S.NonEmptyString255, - version: S.Int, - steps: S.NonEmptyArray(WorkflowStep), - status: S.Union([ - S.TaggedStruct("Queued", { - queuedAt: S.DateFromString - }), - S.TaggedStruct("Running", { - startedAt: S.DateFromString, - activeStepId: S.StringId - }), - S.TaggedStruct("Completed", { - completedAt: S.DateFromString, - result: S.Record(S.String, S.String) - }), - S.TaggedStruct("Failed", { - failedAt: S.DateFromString, - failedStepId: S.StringId, - reason: S.NonEmptyString255 - }) - ]), - history: S.Array( - S.Struct({ - at: S.DateFromString, - stepId: S.optional(S.StringId), - event: S.NonEmptyString255, - details: S.Record(S.String, S.String) - }) - ) - }) -) {} - -// codegen:start {preset: modelFacade, className: _WorkflowRun, schema: S} -export class WorkflowRun - extends S.OpaqueFacade< - WorkflowRun, - WorkflowRun.Encoded, - WorkflowRun.Make, - WorkflowRun.DecodingServices, - WorkflowRun.EncodingServices - >()(_WorkflowRun) -{} -// codegen:end - -// codegen:start {preset: model, static: true, facade: true} -// -export interface WorkflowStep { - readonly id: S.StringId - readonly name: S.NonEmptyString255 - readonly retryPolicy: { - readonly maxAttempts: S.Int - readonly backoff: "fixed" | "exponential" - readonly jitter: false | true - } - readonly input: { readonly [x: string]: string | number | boolean | null } - readonly timeout?: undefined | number -} -export namespace WorkflowStep { - export interface Encoded { - readonly id: string - readonly name: string - readonly retryPolicy: { - readonly maxAttempts: number - readonly backoff: "fixed" | "exponential" - readonly jitter: false | true - } - readonly input: { readonly [x: string]: string | number | boolean | null } - readonly timeout?: undefined | number - } - export interface Make { - readonly id: S.StringId - readonly name: S.NonEmptyString255 - readonly retryPolicy: { - readonly maxAttempts: S.Int - readonly backoff: "fixed" | "exponential" - readonly jitter: false | true - } - readonly input: { readonly [x: string]: string | number | boolean | null } - readonly timeout?: undefined | number - } - export type DecodingServices = never - export type EncodingServices = never -} -export interface WorkflowRun { - readonly id: S.StringId - readonly workflowName: S.NonEmptyString255 - readonly version: S.Int - readonly steps: readonly [WorkflowStep, ...WorkflowStep[]] - readonly status: - | { readonly _tag: "Queued"; readonly queuedAt: Date } - | { readonly _tag: "Running"; readonly startedAt: Date; readonly activeStepId: S.StringId } - | { readonly _tag: "Completed"; readonly completedAt: Date; readonly result: { readonly [x: string]: string } } - | { - readonly _tag: "Failed" - readonly failedAt: Date - readonly failedStepId: S.StringId - readonly reason: S.NonEmptyString255 - } - readonly history: - readonly ({ - readonly at: Date - readonly event: S.NonEmptyString255 - readonly details: { readonly [x: string]: string } - readonly stepId?: undefined | S.StringId - })[] -} -export namespace WorkflowRun { - export interface Encoded { - readonly id: string - readonly workflowName: string - readonly version: number - readonly steps: readonly [WorkflowStep.Encoded, ...WorkflowStep.Encoded[]] - readonly status: - | { readonly _tag: "Queued"; readonly queuedAt: string } - | { readonly _tag: "Running"; readonly startedAt: string; readonly activeStepId: string } - | { readonly _tag: "Completed"; readonly completedAt: string; readonly result: { readonly [x: string]: string } } - | { readonly _tag: "Failed"; readonly failedAt: string; readonly failedStepId: string; readonly reason: string } - readonly history: - readonly ({ - readonly at: string - readonly event: string - readonly details: { readonly [x: string]: string } - readonly stepId?: undefined | string - })[] - } - export interface Make { - readonly id: S.StringId - readonly workflowName: S.NonEmptyString255 - readonly version: S.Int - readonly steps: readonly [WorkflowStep.Make, ...WorkflowStep.Make[]] - readonly status: - | { readonly _tag: "Queued"; readonly queuedAt: Date } - | { readonly _tag: "Running"; readonly startedAt: Date; readonly activeStepId: S.StringId } - | { readonly _tag: "Completed"; readonly completedAt: Date; readonly result: { readonly [x: string]: string } } - | { - readonly _tag: "Failed" - readonly failedAt: Date - readonly failedStepId: S.StringId - readonly reason: S.NonEmptyString255 - } - readonly history: - readonly ({ - readonly at: Date - readonly event: S.NonEmptyString255 - readonly details: { readonly [x: string]: string } - readonly stepId?: undefined | S.StringId - })[] - } - export type DecodingServices = never - export type EncodingServices = never -} -// -// codegen:end diff --git a/packages/eslint-codegen-model/index.js b/packages/eslint-codegen-model/index.js deleted file mode 100644 index 7192ff723..000000000 --- a/packages/eslint-codegen-model/index.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.barrel = require("./dist/presets/barrel").barrel -exports.model = require("./dist/presets/model").model -exports.meta = require("./dist/presets/meta").meta diff --git a/packages/eslint-codegen-model/package.json b/packages/eslint-codegen-model/package.json deleted file mode 100644 index 206eda85f..000000000 --- a/packages/eslint-codegen-model/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "@effect-app/eslint-codegen-model", - "description": "Contains eslint helpers", - "sideEffects": false, - "type": "module", - "version": "2.0.0-beta.27", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/eslint-codegen-model" - }, - "bin": { - "effect-app-codegen": "./dist/cli.js" - }, - "scripts": { - "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && pnpm check", - "check": "tsgo --build", - "build": "tsgo", - "codegen": "pnpm build && node ./dist/cli.js", - "circular": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", - "ncu": "ncu", - "pub": "pnpm publish --access public", - "prepack": "node ../../scripts/stagePublishPackage.mjs prepack", - "postpack": "node ../../scripts/stagePublishPackage.mjs postpack", - "postpublish": "node ../../scripts/stagePublishPackage.mjs postpublish" - }, - "dependencies": { - "glob": "^11.1.0", - "js-yaml": "4.2.0", - "oxc-parser": "0.133.0" - }, - "peerDependencies": { - "typescript": ">=5.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - }, - "devDependencies": { - "@types/js-yaml": "4.0.9", - "madge": "8.0.0", - "oxlint": "1.67.0", - "typescript": "~6.0.3", - "effect-app": "workspace:*" - }, - "main": "index.js", - "exports": { - ".": { - "types": "./src/index.ts", - "default": "./src/index.ts" - }, - "./oxlint": { - "types": "./src/oxlint/index.ts", - "default": "./src/oxlint/index.ts" - } - }, - "publishConfig": { - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./oxlint": { - "types": "./dist/oxlint/index.d.ts", - "default": "./dist/oxlint/index.js" - } - }, - "directory": ".publish", - "linkDirectory": false - }, - "author": "Wessel van der Veen & Scott vanBommel", - "license": "ISC" -} diff --git a/packages/eslint-codegen-model/src/cli.ts b/packages/eslint-codegen-model/src/cli.ts deleted file mode 100644 index 550fa12ad..000000000 --- a/packages/eslint-codegen-model/src/cli.ts +++ /dev/null @@ -1,638 +0,0 @@ -#!/usr/bin/env node -import * as fs from "node:fs" -import * as path from "node:path" - -import { globSync } from "glob" - -import { getExportedModelNames, getFacadeableModelNames } from "./presets/model.ts" -import { applyDefaults, blockRe, type CodegenDefaults, indentBlock, normaliseGeneratedContent, parseBlockOptions, renderPreset, trimTrailingNewline } from "./shared/codegen-block.ts" -import { createNativeModelTypeResolver } from "./shared/native-type-resolver.ts" -import { createModelTypeResolver, type ModelTypeResolver } from "./shared/type-resolver.ts" - -const CONFIG_FILENAMES = ["codegen.config.json"] - -function loadConfig(cwd: string, explicit?: string): CodegenDefaults | undefined { - const candidates = explicit ? [path.resolve(cwd, explicit)] : CONFIG_FILENAMES.map((f) => path.join(cwd, f)) - for (const candidate of candidates) { - if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { - const parsed = JSON.parse(fs.readFileSync(candidate, "utf8")) - return parsed as CodegenDefaults - } - } - if (explicit) { - throw new Error(`Config not found: ${explicit}`) - } - return undefined -} - -const modelBlockRe = /\/\/ codegen:start[ \t]*\{[^}]*\bpreset:\s*model\b/ -const staticTypeModelBlockRe = - /\/\/ codegen:start[ \t]*\{[^}]*\bpreset:\s*model\b[^}]*\bstatic:\s*true\b[^}]*\btype:\s*true\b/ -const staticMakeModelBlockRe = - /\/\/ codegen:start[ \t]*\{[^}]*\bpreset:\s*model\b[^}]*\bstatic:\s*true\b[^}]*\bmake:\s*true\b/ -const facadeModelBlockRe = /\/\/ codegen:start[ \t]*\{[^}]*\bpreset:\s*model\b[^}]*\bfacade:\s*true\b/ - -function escapeRe(s: string): string { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") -} - -type CtorMode = "plain" | "type" | "make" - -/** - * Keep each model's `Opaque` base-class clause in sync with the block mode (idempotent): - * - make → `S.OpaqueShape` - * - type → `S.OpaqueType` - * - plain → `S.Opaque` (revert) - * - * Only `Opaque`-family clauses for the model's own `Self`/`Encoded` are touched (matches - * ``); `Class`/`TaggedClass` models are left untouched. - */ -function syncCtor(source: string, modelNames: ReadonlyArray, mode: CtorMode): string { - let out = source - for (const name of modelNames) { - const n = escapeRe(name) - const re = new RegExp( - `((?:[A-Za-z_$][\\w$]*\\.)?)(?:Opaque|OpaqueType|OpaqueShape)<\\s*${n}(?:\\.Type)?\\s*,\\s*${n}\\.Encoded(?:\\s*,\\s*${n}\\.Make)?\\s*>`, - "g" - ) - const target = mode === "make" - ? `$1OpaqueShape<${name}.Type, ${name}.Encoded, ${name}.Make>` - : mode === "type" - ? `$1OpaqueType<${name}.Type, ${name}.Encoded>` - : `$1Opaque<${name}, ${name}.Encoded>` - out = out.replace(re, target) - } - return out -} - -function findMatchingBrace(source: string, openIndex: number): number { - let depth = 0 - let quote: "\"" | "'" | "`" | undefined - let escaped = false - let lineComment = false - let blockComment = false - - for (let index = openIndex; index < source.length; index++) { - const char = source[index]! - const next = source[index + 1] - - if (lineComment) { - if (char === "\n") lineComment = false - continue - } - if (blockComment) { - if (char === "*" && next === "/") { - blockComment = false - index++ - } - continue - } - if (quote) { - if (escaped) { - escaped = false - } else if (char === "\\") { - escaped = true - } else if (char === quote) { - quote = undefined - } - continue - } - - if (char === "/" && next === "/") { - lineComment = true - index++ - continue - } - if (char === "/" && next === "*") { - blockComment = true - index++ - continue - } - if (char === "\"" || char === "'" || char === "`") { - quote = char - continue - } - if (char === "{") { - depth++ - continue - } - if (char === "}") { - depth-- - if (depth === 0) return index - } - } - - return -1 -} - -function findClassBodyOpen(source: string, start: number): number { - let parenDepth = 0 - let bracketDepth = 0 - let quote: "\"" | "'" | "`" | undefined - let escaped = false - let lineComment = false - let blockComment = false - - for (let index = start; index < source.length; index++) { - const char = source[index]! - const next = source[index + 1] - - if (lineComment) { - if (char === "\n") lineComment = false - continue - } - if (blockComment) { - if (char === "*" && next === "/") { - blockComment = false - index++ - } - continue - } - if (quote) { - if (escaped) { - escaped = false - } else if (char === "\\") { - escaped = true - } else if (char === quote) { - quote = undefined - } - continue - } - - if (char === "/" && next === "/") { - lineComment = true - index++ - continue - } - if (char === "/" && next === "*") { - blockComment = true - index++ - continue - } - if (char === "\"" || char === "'" || char === "`") { - quote = char - continue - } - if (char === "(") { - parenDepth++ - continue - } - if (char === ")") { - parenDepth-- - continue - } - if (char === "[") { - bracketDepth++ - continue - } - if (char === "]") { - bracketDepth-- - continue - } - if (char === "{" && parenDepth === 0 && bracketDepth === 0) { - return index - } - } - - return -1 -} - -function findClassEnd(source: string, start: number): number { - const openIndex = findClassBodyOpen(source, start) - if (openIndex === -1) return -1 - const closeIndex = findMatchingBrace(source, openIndex) - return closeIndex === -1 ? -1 : closeIndex + 1 -} - -function modelSchemaPrefix(classText: string): string { - const match = /\bextends\s+((?:[A-Za-z_$][\w$]*\.)?)(?:Opaque|OpaqueType|OpaqueShape)\s*`, - "g" - ) - const rewritten = classText.replace(re, `$1Opaque<_${name}>`) - // The body moves onto the private `_X`, but the public facade `X` is declared - // AFTER it. Member-access self-references (`X.fields`, `X.someStatic`) would be - // use-before-declaration, so point them at `_X` (structurally equivalent, declared - // first). Keep namespace TYPE members (`X.Encoded`/`Make`/`Type`/services) on `X`. - // NOTE: only the `X.` (member-access) form is rewritten — a bare `\bX\b` rewrite - // would also corrupt `TaggedStruct("X")` tag strings. Bare value self-refs - // (`S.decodeTo(X, ...)`) in a static body are rare; such models stay standard. - const selfValueRef = new RegExp( - `\\b${n}\\.(?!(?:Encoded|Make|Type|DecodingServices|EncodingServices)\\b)`, - "g" - ) - return rewritten.replace(selfValueRef, `_${name}.`) -} - -function facadeClassLine(name: string, prefix: string): string { - return `export class ${name} extends ${prefix}OpaqueFacade<${name}, ${name}.Encoded, ${name}.Make, ${name}.DecodingServices, ${name}.EncodingServices>()(_${name}) {}` -} - -// If `classText` is a no-statics `export class X extends (S.)Opaque()(STRUCT) {}` -// (empty body), return the private as a plain struct const `const _X = STRUCT` -// (lighter than the Opaque class). Returns null for any body content (statics, -// getters, methods) or non-Opaque bases — those keep the class form. -function tryStructPrivate(classText: string, name: string, indent: string): string | null { - const open = findClassBodyOpen(classText, 0) - if (open === -1) return null - const close = findMatchingBrace(classText, open) - if (close === -1) return null - const body = classText.slice(open + 1, close).replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").trim() - if (body.length > 0) return null // has statics/getters/methods -> keep class - // Extract STRUCT from `extends (prefix)Opaque()(STRUCT)`. - const m = /\bextends\s+(?:[A-Za-z_$][\w$]*\.)?Opaque\s*<[^(]*>\s*\(\s*\)\s*\(/.exec(classText.slice(0, open)) - if (!m) return null // OpaqueType/OpaqueShape/Class/etc -> keep class form - const argOpen = m.index + m[0].length - 1 // index of the `(` before STRUCT - let depth = 0 - let k = argOpen - for (; k < classText.length; k++) { - if (classText[k] === "(") depth++ - else if (classText[k] === ")") { - depth-- - if (depth === 0) break - } - } - const struct = classText.slice(argOpen + 1, k).trim() - return `${indent}const _${name} = ${struct}` -} - -function syncFacade(source: string, modelNames: ReadonlyArray, enabled: boolean): string { - let out = source - for (const name of modelNames) { - const n = escapeRe(name) - if (enabled) { - // Base mode: the facade is the generated `class __X extends OpaqueFacade<...>()(_X)` - // (owned by its modelFacade block); the user owns `export class X extends __X { ...statics... }`. - // Leave both alone — only the block preset regenerates `__X`. - const baseMode = new RegExp( - `(^|\\n)\\s*class\\s+__${n}\\s+extends\\s+(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade(?:Class)?\\s*<` - ) - if (baseMode.test(out)) continue - const existingClass = new RegExp( - `(^|\\n)\\s*export\\s+class\\s+${n}\\s+extends\\s+(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade(?:Class)?\\s*<` - ) - const existingConst = new RegExp(`(^|\\n)\\s*export\\s+const\\s+${n}\\s*:\\s*${n}\\.Schema\\s*=`) - if (existingClass.test(out) || existingConst.test(out)) { - // Upgrade a 3-arg facade (``) to the 5-arg form by - // appending the services. Idempotent: when the services are already - // present (group 3), leave the match untouched so we don't collapse a - // dprint-wrapped multi-line declaration back to one line (which would - // oscillate with the formatter forever). - out = out.replace( - new RegExp( - `(OpaqueFacade(?:Class)?<\\s*)${n}(?:\\.Type)?(\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make)((?:\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices)?)(\\s*>)`, - "g" - ), - (match, p1: string, p2: string, services: string, p3: string) => - services ? match : `${p1}${name}${p2}, ${name}.DecodingServices, ${name}.EncodingServices${p3}` - ) - const privateClassRe = new RegExp(`(^|\\n)(\\s*)class\\s+_${n}\\b`) - const privateMatch = privateClassRe.exec(out) - if (privateMatch) { - const start = privateMatch.index + privateMatch[1]!.length - const end = findClassEnd(out, start) - if (end !== -1) { - const pIndent = privateMatch[2]! - const ct = out.slice(start, end) - const replacement = tryStructPrivate(ct, name, pIndent) ?? syncFacadeSourceCtor(ct, name) - out = `${out.slice(0, start)}${replacement}${out.slice(end)}` - } - } - const facadeBlock = new RegExp( - `// codegen:start[^\\n]*\\{[^}]*\\bpreset:\\s*modelFacade\\b[^}]*\\bclassName:\\s*_${n}\\b[^}]*\\}[\\s\\S]*?export\\s+(?:const|class)\\s+${n}\\b` - ) - if (!facadeBlock.test(out)) { - const facadeLine = new RegExp( - `(^|\\n)([ \\t]*)export\\s+const\\s+${n}\\s*:\\s*${n}\\.Schema\\s*=\\s*((?:(?:[A-Za-z_$][\\w$]*\\.)?)OpaqueFacade<\\s*${n}\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make(?:\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices)?\\s*>\\(\\)\\(\\s*_${n}\\s*\\))` - ) - out = out.replace(facadeLine, (_match, lineStart: string, indent: string, expression: string) => { - const prefix = /^((?:[A-Za-z_$][\w$]*\.)?)OpaqueFacade/.exec(expression)?.[1] ?? "" - return [ - `${lineStart}${indent}// codegen:start {preset: modelFacade, className: _${name}${schemaOption(prefix)}}`, - `${indent}${facadeClassLine(name, prefix)}`, - `${indent}// codegen:end` - ] - .join("\n") - }) - } - continue - } - - const classRe = new RegExp(`(^|\\n)(\\s*)export\\s+class\\s+${n}\\b`) - const match = classRe.exec(out) - if (!match) continue - const start = match.index + match[1]!.length - const end = findClassEnd(out, start) - if (end === -1) continue - - const classText = out.slice(start, end) - const indent = match[2]! - const prefix = modelSchemaPrefix(classText) - // No-statics `S.Opaque` model -> emit the private as a plain `S.Struct` const - // (lighter type; ~-14.5% definition instantiations). The facade class still - // wraps it and is constructable (OpaqueFacade uses overloads for class vs struct schemas). - const privateClass = tryStructPrivate(classText, name, indent) ?? syncFacadeSourceCtor( - classText.replace(new RegExp(`^${indent}export\\s+class\\s+${n}\\b`), `${indent}class _${name}`), - name - ) - const facade = [ - `${indent}// codegen:start {preset: modelFacade, className: _${name}${schemaOption(prefix)}}`, - `${indent}${facadeClassLine(name, prefix)}`, - `${indent}// codegen:end` - ] - .join("\n") - out = `${out.slice(0, start)}${privateClass}\n${facade}${out.slice(end)}` - } else { - const classRe = new RegExp(`(^|\\n)(\\s*)class\\s+_${n}\\b`) - const match = classRe.exec(out) - if (!match) continue - const start = match.index + match[1]!.length - const end = findClassEnd(out, start) - if (end === -1) continue - - const classText = out.slice(start, end) - const indent = match[2]! - const exportedClass = classText.replace( - new RegExp(`^${indent}class\\s+_${n}\\b`), - `${indent}export class ${name}` - ) - const facadeRe = new RegExp( - `\\n${indent}(?:// codegen:start[^\\n]*\\{[^}]*\\bpreset:\\s*modelFacade\\b[^}]*\\}\\n)?${indent}export\\s+(?:const\\s+${n}\\s*:\\s*${n}\\.Schema\\s*=\\s*(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade<\\s*${n}(?:\\.Type)?\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make(?:\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices)?\\s*>\\(\\)\\(\\s*_${n}\\s*\\)|class\\s+${n}\\s+extends\\s+(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade(?:Class)?<\\s*${n}\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices\\s*>\\(\\)\\(\\s*_${n}\\s*\\)\\s*\\{\\})(?:\\n${indent}// codegen:end)?` - ) - out = `${out.slice(0, start)}${exportedClass}${out.slice(end)}`.replace(facadeRe, "") - } - } - return out -} - -function syncModelSource(source: string): string { - // Sync each model's Opaque ctor to the block mode (make → OpaqueShape, type → OpaqueType, - // else plain Opaque). Done outside codegen blocks, on the class declarations. Only for files - // that actually contain a model codegen block, so manual OpaqueType/Shape usage is untouched. - if (modelBlockRe.test(source)) { - const facade = facadeModelBlockRe.test(source) - const mode: CtorMode = staticMakeModelBlockRe.test(source) - ? "make" - : staticTypeModelBlockRe.test(source) - ? "type" - : "plain" - const modelNames = getExportedModelNames(source) - if (facade) { - // Only rewrite Opaque-struct models into facades; leave Class-based models - // as-is so a mixed file still converts the facade-able ones. - return syncFacade(source, getFacadeableModelNames(source), true) - } - return syncCtor(syncFacade(source, modelNames, false), modelNames, mode) - } - return source -} - -function updateFile( - filePath: string, - source: string, - defaults?: CodegenDefaults, - resolver?: ModelTypeResolver -): boolean { - let changed = false - - const synced = syncModelSource(source) - if (synced !== source) { - changed = true - source = synced - } - - const next = source.replace( - blockRe, - (_match, indent: string, rawOptions: string, body: string, endIndent: string) => { - const options = applyDefaults(parseBlockOptions(rawOptions), defaults) - const existingContent = trimTrailingNewline(body) - const generatedContent = trimTrailingNewline( - normaliseGeneratedContent( - options, - filePath, - renderPreset(options, { filename: filePath, existingContent }, source, resolver) - ) - ) - - const nextBody = generatedContent.length > 0 ? `${indentBlock(generatedContent, indent)}\n` : "" - const replacement = `${indent}// codegen:start ${rawOptions}\n${nextBody}${endIndent}// codegen:end` - - if (replacement !== _match) { - changed = true - } - - return replacement - } - ) - - if (next !== source) { - fs.writeFileSync(filePath, next) - } - - return changed -} - -interface ParsedArgs { - files: Array - help: boolean - config?: string - tsconfig?: string - native?: boolean -} - -function parseArgs(args: ReadonlyArray): ParsedArgs { - const files: Array = [] - let config: string | undefined - let tsconfig: string | undefined - // Native (tsgo) is the default resolver; `--legacy` opts back into the classic - // `typescript` Compiler API. `--native` is kept as an accepted no-op. - let native = true - - for (let index = 0; index < args.length; index++) { - const part = args[index]! - if (part === "--help" || part === "-h") { - return { files, help: true } - } - if (part === "--file") { - const next = args[index + 1] - if (!next) { - throw new Error("Missing value for --file") - } - files.push(path.resolve(process.cwd(), next)) - index++ - continue - } - if (part === "--config") { - const next = args[index + 1] - if (!next) { - throw new Error("Missing value for --config") - } - config = next - index++ - continue - } - if (part === "--tsconfig") { - const next = args[index + 1] - if (!next) { - throw new Error("Missing value for --tsconfig") - } - tsconfig = path.resolve(process.cwd(), next) - index++ - continue - } - if (part === "--native") { - native = true - continue - } - if (part === "--legacy" || part === "--classic") { - native = false - continue - } - throw new Error(`Unknown argument: ${part}`) - } - - const result: ParsedArgs = { files, help: false, native } - if (config !== undefined) result.config = config - if (tsconfig !== undefined) result.tsconfig = tsconfig - return result -} - -// A model block requests type-checker-backed literal interfaces via `static: true` -// or a shallow facade. -const staticModelBlockRe = /\/\/ codegen:start[ \t]*\{[^}]*\bpreset:\s*model\b[^}]*\b(?:static|facade):\s*true\b/ - -function defaultFiles(): Array { - return globSync("**/*.{ts,tsx,mts,cts}", { - cwd: process.cwd(), - nodir: true, - ignore: [ - "**/node_modules/**", - "**/dist/**", - "**/build/**", - "**/.git/**" - ] - }) - .map((filePath) => path.resolve(process.cwd(), filePath)) -} - -function findNearestTsconfig(fromDir: string): string | undefined { - let dir = fromDir - for (;;) { - const candidate = path.join(dir, "tsconfig.json") - if (fs.existsSync(candidate)) return candidate - const parent = path.dirname(dir) - if (parent === dir) return undefined - dir = parent - } -} - -function run() { - const { config, files, help, native, tsconfig } = parseArgs(process.argv.slice(2)) - - if (help) { - console.log("Usage: effect-app-codegen [--file ]... [--config ] [--tsconfig ] [--legacy]") - console.log("Runs codegen blocks in the given files, or scans the current working tree when omitted.") - console.log(`Loads ${CONFIG_FILENAMES.join(", ")} from cwd when present; --config overrides.`) - console.log( - "static/facade blocks resolve via the native tsgo fork by default; --legacy uses the classic typescript API." - ) - console.log("--tsconfig enables `static` model blocks (type-checker-backed literal Encoded/Type).") - return - } - - const defaults = loadConfig(process.cwd(), config) - const targetFiles = files.length > 0 ? files : defaultFiles() - const updated: Array = [] - const untouched: Array = [] - - // Build the type resolver lazily, only if some target file requests static model blocks. - const candidateFiles = targetFiles.filter((f) => fs.existsSync(f) && fs.statSync(f).isFile()) - const staticFiles = candidateFiles.filter((f) => staticModelBlockRe.test(fs.readFileSync(f, "utf8"))) - const preSynced = new Set() - for (const filePath of staticFiles) { - const source = fs.readFileSync(filePath, "utf8") - const synced = syncModelSource(source) - if (synced !== source) { - fs.writeFileSync(filePath, synced) - preSynced.add(filePath) - } - } - const tsconfigPath = staticFiles.length > 0 - ? (tsconfig ?? findNearestTsconfig(path.dirname(staticFiles[0]!))) - : undefined - if (staticFiles.length > 0 && !tsconfigPath) { - throw new Error("static model blocks require a tsconfig; pass --tsconfig ") - } - - // Static/facade blocks resolve a model's generated `Encoded`/`Make`/services - // from its NESTED models' generated namespaces. A composite model whose nested - // models are generated in the same run only resolves correctly once those - // namespaces exist, so we iterate until the static files reach a fixed point - // (recreating the resolver each round to pick up freshly-written namespaces). - const updatedSet = new Set() - const MAX_ROUNDS = 5 - let round = 0 - let changedStatic = true - while (round === 0 || (changedStatic && round < MAX_ROUNDS)) { - const firstRound = round === 0 - round++ - changedStatic = false - const resolver: ModelTypeResolver | undefined = tsconfigPath - ? native - ? createNativeModelTypeResolver({ tsconfigPath }) - : createModelTypeResolver({ tsconfigPath, files: staticFiles }) - : undefined - - // First round: process every target (incl. non-static presets). Later rounds: - // only re-resolve static files (the rest are already at a fixed point). - const filesThisRound = firstRound ? targetFiles : staticFiles - for (const filePath of filesThisRound) { - if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { - throw new Error(`File not found: ${filePath}`) - } - const source = fs.readFileSync(filePath, "utf8") - if (!source.includes("// codegen:start")) continue - if (updateFile(filePath, source, defaults, resolver)) { - updatedSet.add(path.relative(process.cwd(), filePath)) - if (staticFiles.includes(filePath)) changedStatic = true - } else if (firstRound && preSynced.has(filePath)) { - updatedSet.add(path.relative(process.cwd(), filePath)) - } - } - } - for (const f of updatedSet) updated.push(f) - for (const f of targetFiles) { - const rel = path.relative(process.cwd(), f) - if (!updatedSet.has(rel) && fs.existsSync(f) && fs.readFileSync(f, "utf8").includes("// codegen:start")) { - untouched.push(rel) - } - } - - console.log(`codegen: ${updated.length} updated, ${untouched.length} unchanged`) - for (const filePath of updated) { - console.log(`updated ${filePath}`) - } -} - -try { - run() -} catch (error) { - const message = error instanceof Error ? error.message : String(error) - console.error(message) - process.exitCode = 1 -} diff --git a/packages/eslint-codegen-model/src/index.ts b/packages/eslint-codegen-model/src/index.ts deleted file mode 100644 index ffc8f9204..000000000 --- a/packages/eslint-codegen-model/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// codegen:start {preset: barrel, include: ./*/, nodir: false} -export * from "./oxlint/index.ts" -export * from "./presets/index.ts" -// codegen:end diff --git a/packages/eslint-codegen-model/src/js-yaml.d.ts b/packages/eslint-codegen-model/src/js-yaml.d.ts deleted file mode 100644 index 2a5e7b5a3..000000000 --- a/packages/eslint-codegen-model/src/js-yaml.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module "js-yaml" { - export function load(input: string): unknown -} diff --git a/packages/eslint-codegen-model/src/normalise.ts b/packages/eslint-codegen-model/src/normalise.ts deleted file mode 100644 index 481896edf..000000000 --- a/packages/eslint-codegen-model/src/normalise.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { parseSync } from "oxc-parser" - -function stripNoise(value: unknown): unknown { - if (Array.isArray(value)) { - return value.map(stripNoise) - } - - if (!value || typeof value !== "object") { - return value - } - - const entries = Object.entries(value).filter(([key]) => - key !== "end" - && key !== "loc" - && key !== "range" - && key !== "raw" - && key !== "start" - ) - - return Object.fromEntries(entries.map(([key, child]) => [key, stripNoise(child)])) -} - -export function normaliseModule(code: string, filename: string) { - return JSON.stringify( - stripNoise( - parseSync(filename, code, { - lang: "ts", - preserveParens: false, - sourceType: "module" - }) - .program - ) - ) -} - -export function normaliseModuleForBarrel(code: string, filename: string) { - return normaliseModule(code.replace(/\/index\b/g, ""), filename) -} diff --git a/packages/eslint-codegen-model/src/oxlint/index.ts b/packages/eslint-codegen-model/src/oxlint/index.ts deleted file mode 100644 index 7723d6880..000000000 --- a/packages/eslint-codegen-model/src/oxlint/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import codegenRule from "./rules/codegen.ts" - -export default { - meta: { name: "codegen" }, - rules: { - codegen: codegenRule - } -} diff --git a/packages/eslint-codegen-model/src/oxlint/rules/codegen.ts b/packages/eslint-codegen-model/src/oxlint/rules/codegen.ts deleted file mode 100644 index ba416b6d0..000000000 --- a/packages/eslint-codegen-model/src/oxlint/rules/codegen.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as fs from "node:fs" -import * as path from "node:path" -// @ts-expect-error oxlint@1.61.0 declares CreateRule but does not export it. -import type { CreateRule } from "oxlint/plugins-dev" -import { applyDefaults, type BlockOptions, blockRe, type CodegenDefaults, indentBlock, normaliseGeneratedContent, parseBlockOptions, renderPreset, trimTrailingNewline } from "../../shared/codegen-block.ts" -import { createNativeModelTypeResolver } from "../../shared/native-type-resolver.ts" -import type { ModelTypeResolver } from "../../shared/type-resolver.ts" - -/** Nearest `tsconfig.json` walking up from `from`, or null. */ -function findNearestTsconfig(from: string): string | null { - let dir = path.dirname(from) - for (;;) { - const candidate = path.join(dir, "tsconfig.json") - if (fs.existsSync(candidate)) return candidate - const parent = path.dirname(dir) - if (parent === dir) return null - dir = parent - } -} - -/** - * Editor/CI type-aware resolver, opt-in via `TSGOLINT_CODEGEN_BIN`. When set, - * the oxlint codegen rule resolves `static`/`facade` model blocks through the - * tsgo fork -- so facade interfaces stay live in the editor without running the - * CLI. Without the env var the rule keeps its text-only behaviour (no checker, - * static/facade blocks left untouched). Cached per tsconfig per process. - */ -const nativeResolvers = new Map() -function nativeResolverFor(filename: string): ModelTypeResolver | undefined { - // Native (tsgo) is the default: the binary ships as the `oxlint-tsgolint` - // drop-in, and the resolver is lazy (no binary work until a static/facade - // block actually calls it). Set `TSGOLINT_CODEGEN_OFF` to fall back to - // text-only (static/facade blocks left untouched). `TSGOLINT_CODEGEN_BIN` - // overrides the binary path. - if (process.env["TSGOLINT_CODEGEN_OFF"]) return undefined - const tsconfigPath = findNearestTsconfig(filename) - if (!tsconfigPath) return undefined - if (!nativeResolvers.has(tsconfigPath)) { - try { - nativeResolvers.set(tsconfigPath, createNativeModelTypeResolver({ tsconfigPath })) - } catch { - nativeResolvers.set(tsconfigPath, null) - } - } - return nativeResolvers.get(tsconfigPath) ?? undefined -} - -type RuleContext = { - sourceCode: { - getText: () => string - } - physicalFilename: string - options: ReadonlyArray - report: (diagnostic: { - node: RuleNode - message: string - fix: (fixer: RuleFixer) => { range: [number, number]; text: string } - }) => void -} - -type RuleNode = { - start: number - end: number - range: [number, number] -} - -type RuleFixer = { - replaceTextRange: (range: [number, number], text: string) => { range: [number, number]; text: string } -} - -const codegenRule: CreateRule = { - meta: { - type: "suggestion", - fixable: "code", - schema: [{ - type: "object", - additionalProperties: { - type: "object", - additionalProperties: true - } - }], - docs: { - description: "Ensure codegen blocks are up to date" - } - }, - create(context: RuleContext) { - const defaults = (context.options[0] ?? undefined) as CodegenDefaults | undefined - return { - Program(program: RuleNode) { - const source = context.sourceCode.getText() - const filename = context.physicalFilename - const resolver = nativeResolverFor(filename) - - // Create a fresh regex instance per Program visit to avoid shared lastIndex state - const re = new RegExp(blockRe.source, blockRe.flags) - let match: RegExpExecArray | null - - while ((match = re.exec(source)) !== null) { - const [fullMatch, indent = "", rawOptions = "", body = "", endIndent = ""] = match - const matchStart = match.index - const matchEnd = match.index + fullMatch.length - - let options: BlockOptions - try { - options = applyDefaults(parseBlockOptions(rawOptions), defaults) - } catch { - continue - } - - const existingContent = trimTrailingNewline(body) - let generatedContent: string - try { - generatedContent = trimTrailingNewline( - normaliseGeneratedContent( - options, - filename, - renderPreset(options, { filename, existingContent }, source, resolver) - ) - ) - } catch { - continue - } - - const nextBody = generatedContent.length > 0 ? `${indentBlock(generatedContent, indent)}\n` : "" - const replacement = `${indent}// codegen:start ${rawOptions}\n${nextBody}${endIndent}// codegen:end` - - if (replacement !== fullMatch) { - context.report({ - node: program, - message: `codegen block with preset "${options.preset}" is stale`, - fix(fixer: RuleFixer) { - return fixer.replaceTextRange([matchStart, matchEnd], replacement) - } - }) - } - } - } - } - } -} - -export default codegenRule diff --git a/packages/eslint-codegen-model/src/presets/barrel.ts b/packages/eslint-codegen-model/src/presets/barrel.ts deleted file mode 100644 index 068caa045..000000000 --- a/packages/eslint-codegen-model/src/presets/barrel.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { globSync } from "glob" -import * as path from "path" -import { normaliseModuleForBarrel } from "../normalise.ts" - -// dprint's default import/export sort: case-insensitive, then case-sensitive as -// a tie-break (uppercase before lowercase). Plain lexicographic, not numeric. -function bySpecifier(a: string, b: string): number { - const la = a.toLowerCase() - const lb = b.toLowerCase() - if (la !== lb) return la < lb ? -1 : 1 - return a < b ? -1 : a > b ? 1 : 0 -} - -type PresetFn> = (args: { - meta: { filename: string; existingContent: string } - options: T -}, context?: unknown) => string - -function last(list: readonly T[]): T | undefined { - return list[list.length - 1] -} - -function splitWords(s: string): string[] { - return s - .replace(/([a-z\d])([A-Z])/g, "$1 $2") - .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2") - .split(/[^a-zA-Z0-9]+/) - .filter((w) => w.length > 0) -} - -function toCamelCase(s: string): string { - return splitWords(s) - .map((word, i) => { - const lower = word.toLowerCase() - return i === 0 ? lower : lower[0]!.toUpperCase() + lower.slice(1) - }) - .join("") -} - -function toPascalCase(s: string): string { - return splitWords(s) - .map((word) => word[0]!.toUpperCase() + word.slice(1).toLowerCase()) - .join("") -} - -/** - * Bundle several modules into a single convenient one. - * - * @example - * // codegen:start {preset: barrel, include: some/path/*.ts, exclude: some/path/*util.ts} - * export * from './some/path/module-a' - * export * from './some/path/module-b' - * export * from './some/path/module-c' - * // codegen:end - * - * @param include - * [optional] If specified, the barrel will only include file paths that match this glob pattern - * @param exclude - * [optional] If specified, the barrel will exclude file paths that match these glob patterns - * @param import - * [optional] If specified, matching files will be imported and re-exported rather than directly exported - * with `export * from './xyz'`. Use `import: star` for `import * as xyz from './xyz'` style imports. - * Use `import: default` for `import xyz from './xyz'` style imports. - * @param export - * [optional] Only valid if the import style has been specified (either `import: star` or `import: default`). - * If specified, matching modules will be bundled into a const or default export based on this name. If set - * to `{name: someName, keys: path}` the relative file paths will be used as keys. Otherwise the file paths - * will be camel-cased to make them valid js identifiers. - * @param importExtension - * [optional] Extension used on the emitted import/export specifiers. Defaults to `.ts`. Set to `.js` (or - * empty string) to emit unsuffixed/TS specifiers. Configurable per block, or globally via the rule option - * `{ barrel: { importExtension: ".js" } }` / `codegen.config.json`. - */ -export const barrel: PresetFn<{ - include?: string - exclude?: string | string[] - import?: "default" | "star" - export?: - | string - | { name: string; keys: "path" | "camelCase" } - | { as: "PascalCase"; postfix?: string } - nodir?: boolean - modulegen?: boolean - importExtension?: string -}> = ({ meta, options: opts }) => { - const cwd = path.dirname(meta.filename) - const nodir = opts.nodir ?? true - const modulegen = opts.modulegen ?? false - const importExt = opts.importExtension ?? ".ts" - - const ext = meta.filename.split(".").slice(-1)[0] - const pattern = opts.include || `*.${ext}` - - const relativeFiles = globSync(pattern, { cwd, nodir, ...(opts.exclude ? { ignore: opts.exclude } : {}) }) - .filter((f) => path.resolve(cwd, f) !== path.resolve(meta.filename)) - .map((f) => `./${f}`.replace(/(\.\/)+\./g, ".")) - .filter((file) => - nodir - ? [".js", ".mjs", ".ts", ".tsx"].includes(path.extname(file)) - : true - ) - .map((f) => { - const isDir = f.endsWith("/") - const cleaned = f.replace(/\.\w+$/, "").replace(/\/$/, "") - return isDir ? `${cleaned}/index` : cleaned - }) - // Match dprint's module-specifier sort so the generated barrel and the - // formatter agree (otherwise lint-fix reorders the exports and codegen then - // flags them as stale). dprint sorts case-insensitively, tie-breaking by the - // original case (uppercase first); it is NOT numeric (`a10` before `a2`). - .sort(bySpecifier) - - let expectedContent: string - - if (opts.import === undefined) { - const exportOpt = opts.export - if ( - typeof exportOpt === "object" - && exportOpt !== null - && "as" in exportOpt - && exportOpt.as === "PascalCase" - ) { - expectedContent = relativeFiles - .map( - (f) => - `export * as ${toPascalCase(last(f.split("/"))!)}${ - "postfix" in exportOpt ? exportOpt.postfix : "" - } from "${f}${importExt}"` - ) - .join("\n") - } else { - expectedContent = relativeFiles.map((f) => `export * from "${f}${importExt}"`).join("\n") - } - } else { - const importPrefix = opts.import === "default" ? "" : "* as " - const rawIdentifiers = relativeFiles.map((f) => ({ - file: f, - identifier: toCamelCase(modulegen ? last(f.split("/"))! : f) - .replace(/^([^a-z])/, "_$1") - .replace(/([\^/])Index$/, "$1") - })) - - const grouped = rawIdentifiers.reduce>>( - (acc, info) => { - ;(acc[info.identifier] ??= []).push(info) - return acc - }, - {} - ) - const withIdentifiers = Object.values(grouped).flatMap((group) => - group.length === 1 - ? group - : group.map((info, i) => ({ ...info, identifier: `${info.identifier}_${i + 1}` })) - ) - - const imports = withIdentifiers - .map((i) => `import ${importPrefix}${i.identifier} from "${i.file}${importExt}"`) - .join("\n") - - const exportOpt = opts.export - const exportProps = modulegen - ? [] - : typeof exportOpt === "object" - && exportOpt !== null - && "keys" in exportOpt - && (exportOpt as { name: string; keys: "path" | "camelCase" }).keys === "path" - ? withIdentifiers.map((i) => `${JSON.stringify(i.file)}: ${i.identifier}`) - : withIdentifiers.map((i) => i.identifier) - - let exportPrefix: string - if (exportOpt === undefined) { - exportPrefix = "export" - } else if (exportOpt === "default") { - exportPrefix = "export default" - } else if (typeof exportOpt === "object" && "name" in exportOpt && exportOpt.name === "default") { - exportPrefix = "export default" - } else if (typeof exportOpt === "string") { - exportPrefix = `export const ${exportOpt} =` - } else if (typeof exportOpt === "object" && "name" in exportOpt) { - exportPrefix = `export const ${exportOpt.name} =` - } else { - exportPrefix = "" - } - - const exports = exportProps.join(",\n ") - - const moduleGen = withIdentifiers - .map((i) => { - const up = `${i.identifier[0]!.toUpperCase()}${i.identifier.slice(1)}` - return `export interface ${up} extends Id {} -export const ${up}: ${up} = ${i.identifier}` - }) - .join("\n") - - const exportss = modulegen ? "" : `\n${exportPrefix} {\n ${exports}\n}` - expectedContent = `${imports}\n${exportss}\n${ - modulegen && moduleGen - ? "type Id = T\n/* eslint-disable @typescript-eslint/no-empty-object-type */\n\n" + moduleGen - : "" - }` - } - - // Fast path: exact match after trimming (avoids AST parse) - if (expectedContent.trim() === meta.existingContent.trim()) { - return meta.existingContent - } - - // Slow path: AST-based comparison (handles /index equivalence etc.) - try { - if ( - normaliseModuleForBarrel(expectedContent, meta.filename) - === normaliseModuleForBarrel(meta.existingContent, meta.filename) - ) { - return meta.existingContent - } - } catch {} - - return expectedContent -} diff --git a/packages/eslint-codegen-model/src/presets/index.ts b/packages/eslint-codegen-model/src/presets/index.ts deleted file mode 100644 index 11f4b7d69..000000000 --- a/packages/eslint-codegen-model/src/presets/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// codegen:start {preset: barrel, include: ./*.ts} -export * from "./barrel.ts" -export * from "./meta.ts" -export * from "./model-facade.ts" -export * from "./model.ts" -// codegen:end diff --git a/packages/eslint-codegen-model/src/presets/meta.ts b/packages/eslint-codegen-model/src/presets/meta.ts deleted file mode 100644 index dc0dec56e..000000000 --- a/packages/eslint-codegen-model/src/presets/meta.ts +++ /dev/null @@ -1,48 +0,0 @@ -type PresetFn> = (args: { - meta: { filename: string; existingContent: string } - options: T -}, context?: unknown) => string - -type MetaPresetOptions = { - sourcePrefix?: string - stripSuffixes?: ReadonlyArray -} - -const filterAdjacent = (input: string[]) => input.filter((i, idx) => input[idx - 1] !== i) - -const stripConfiguredSuffix = (input: string, suffixes: ReadonlyArray) => { - const match = suffixes.find((_) => input.endsWith(_)) - return match ? input.slice(0, -match.length) : input -} - -const moduleNameFromFilename = (filename: string, sourcePrefix: string, stripSuffixes: ReadonlyArray) => { - const modulePath = stripConfiguredSuffix( - filename - .substring(filename.indexOf(sourcePrefix) + sourcePrefix.length, filename.length - 3) - .split("/") - .join("/"), - stripSuffixes - ) - - return filterAdjacent(modulePath.split("/")) - .filter((_) => _ !== "resources") - .join("/") -} - -/** - * Adds file meta - */ -export const meta: PresetFn = ({ meta, options }) => { - const sourcePrefix = options.sourcePrefix || "src/" - const stripSuffixes = options.stripSuffixes ?? [] - const moduleName = moduleNameFromFilename(meta.filename, sourcePrefix, stripSuffixes) - const expectedContent = `const Req = TaggedRequestFor("${moduleName}")` - - try { - if (expectedContent === meta.existingContent) { - return meta.existingContent - } - } catch {} - - return expectedContent -} diff --git a/packages/eslint-codegen-model/src/presets/model-facade.ts b/packages/eslint-codegen-model/src/presets/model-facade.ts deleted file mode 100644 index f787535b3..000000000 --- a/packages/eslint-codegen-model/src/presets/model-facade.ts +++ /dev/null @@ -1,48 +0,0 @@ -export type ModelFacadeOptions = { - readonly className?: string - readonly name?: string - readonly schema?: string - /** - * Base mode: emit a non-exported base `class __X extends OpaqueFacade<...>()(_X) {}` - * instead of `export class X`. The user owns `export class X extends __X { ...statics... }`, - * so static/instance members live on the public class while `_X` stays a light schema. - */ - readonly base?: boolean -} - -// Whitespace-insensitive equality: the generated class line is long, so dprint wraps the -// `extends OpaqueFacade<...>` type args across multiple lines. Comparing stripped of -// all whitespace lets a dprint-formatted block match the single-line form, so codegen leaves -// it alone instead of reverting it (which would just re-wrap → codegen/dprint oscillation). -const stripWs = (s: string): string => s.replace(/\s+/g, "") - -export function modelFacade( - { meta, options }: { meta?: { existingContent: string }; options: ModelFacadeOptions } -): string { - const className = options.className - if (typeof className !== "string" || className.length === 0) { - return "/** modelFacade requires `className` */" - } - const name = typeof options.name === "string" && options.name.length > 0 - ? options.name - : className.startsWith("_") - ? className.slice(1) - : className - const schema = typeof options.schema === "string" ? options.schema : "S" - const prefix = schema.length > 0 ? `${schema}.` : "" - const lhs = options.base === true ? `class __${name}` : `export class ${name}` - const decl = - `${lhs} extends ${prefix}OpaqueFacade<${name}, ${name}.Encoded, ${name}.Make, ${name}.DecodingServices, ${name}.EncodingServices>()(${className}) {}` - // The exported facade `class ${name}` merges with the generated `export interface ${name}` - // (the top-level instance shape from the `model` facade preset) — that merge is intentional, - // so suppress no-unsafe-declaration-merging. Base mode emits a private `class __${name}` that - // doesn't merge (the user's own `export class ${name} extends __${name}` owns the disable). - const expected = options.base === true - ? decl - : `// eslint-disable-next-line typescript/no-unsafe-declaration-merging\n${decl}` - // Preserve the dprint-formatted block when it's equivalent (see stripWs above). - if (meta && stripWs(meta.existingContent) === stripWs(expected)) { - return meta.existingContent - } - return expected -} diff --git a/packages/eslint-codegen-model/src/presets/model.ts b/packages/eslint-codegen-model/src/presets/model.ts deleted file mode 100644 index bbaa52901..000000000 --- a/packages/eslint-codegen-model/src/presets/model.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as fs from "fs" -import type { ModelTypeResolver } from "../shared/type-resolver.ts" - -// Detects `export class Foo` whose extends clause contains e.g. `Class { - const result: Array = [] - const add = (name: string) => { - if (!result.includes(name)) result.push(name) - } - const classRe = /(^|\n)\s*export\s+class\s+(\w+)/g - const matches = Array.from(code.matchAll(classRe)) - for (const [index, match] of matches.entries()) { - const name = match[2]! - const start = match.index + match[1]!.length - // Take up to the next `export class` or 500 chars, whichever comes first, - // then trim further to only the extends clause (before the first `{`). - const nextClass = matches[index + 1]?.index - const rawWindow = code.slice(start, nextClass === undefined ? start + 500 : nextClass) - // Only look at the part before the class body opens. - const braceIdx = rawWindow.indexOf("{") - const extendsWindow = braceIdx === -1 ? rawWindow : rawWindow.slice(0, braceIdx) - if ( - baseClassWithEncodedRe.test(extendsWindow) - || (opaqueWithEncodedRe.test(extendsWindow) && !contextOpaqueRe.test(extendsWindow)) - || opaqueFacadeRe.test(extendsWindow) - // base mode: `export class X extends __X` (facade lives on the generated `__X`) - || new RegExp(`extends\\s+__${name}\\b`).test(extendsWindow) - ) { - add(name) - } - } - const facadeRe = /(^|\n)\s*export\s+const\s+(\w+)\s*:\s*\2\.Schema\s*=/g - for (const match of code.matchAll(facadeRe)) { - add(match[2]!) - } - return result -} - -// The extends-clause text of a model's defining class — checks the private `_X` -// (post-rewrite) first, then the exported `X` (pre-rewrite / already-facade). -function modelExtendsWindow(code: string, name: string): string | null { - // `class __X` first: base mode (`export class X extends __X`) where the facade - // lives on the generated base `class __X extends OpaqueFacade<...>()(_X)`. - for (const decl of [`class __${name}`, `class _${name}`, `export class ${name}`, `class ${name}`]) { - const re = new RegExp(`(^|\\n)\\s*${decl.replace(/[$]/g, "\\$&")}\\b`) - const m = re.exec(code) - if (!m) continue - const start = m.index + m[1]!.length - const window = code.slice(start, start + 500) - const braceIdx = window.indexOf("{") - return braceIdx === -1 ? window : window.slice(0, braceIdx) - } - return null -} - -// Models that can be turned into a shallow facade: those whose underlying schema -// is `S.Opaque(...)` (or already an `OpaqueFacade`). `Class`/`TaggedClass`/etc. -// models are nominal (and may carry instance methods) — leave them standard. -export function getFacadeableModelNames(code: string): Array { - return getExportedModelNames(code).filter((name) => { - const w = modelExtendsWindow(code, name) - if (w === null) return false - if (baseClassWithEncodedRe.test(w)) return false - return opaqueFacadeRe.test(w) || (/(?:^|[\s.])Opaque\s*`. Greatly reduces instantiation - * on Encoded-touching consumers. Requires a type resolver (CLI only); without one (e.g. the - * oxlint rule) static blocks are left untouched. - */ - static?: boolean - /** - * With `static`, also emit a literal `Type` interface (decoded side); the class is - * rewritten to `S.OpaqueType` so the instance uses it. - */ - type?: boolean - /** - * With `static`, also emit a literal `Make` interface (make-input side); the class is - * rewritten to `S.OpaqueShape`. Implies `type`. - */ - make?: boolean - /** - * With `static`, emit a shallow exported facade for private model classes. Implies - * `type` and `make`; the CLI rewrites `export class X` into private `class _X` - * plus `export class X extends S.OpaqueFacade()(_X) {}`. - */ - facade?: boolean - /** @deprecated unused */ - writeFullTypes?: boolean -} - -export function model( - { meta, options }: { meta: { filename: string; existingContent: string }; options: ModelOptions }, - context?: unknown, - resolver?: ModelTypeResolver -): string { - try { - const targetContent = typeof context === "string" && context.length > 0 - ? context - : fs.readFileSync(meta.filename).toString() - - const modelNames: Array = [] - const seen = new Set() - for (const modelName of getExportedModelNames(targetContent)) { - if (seen.has(modelName)) continue - seen.add(modelName) - modelNames.push(modelName) - } - - let expectedContent: string - // Any facade option (static/type/make/facade) needs the type checker. Without a - // resolver (e.g. the oxlint rule, no checker) leave the block untouched so we never - // revert CLI-generated static Encoded/Type/Make interfaces to the conditional form. - const needsResolver = !!(options?.static || options?.type || options?.make || options?.facade) - if (needsResolver) { - if (!resolver) { - return meta.existingContent - } - // In facade mode, only Opaque-struct models become facades; Class-based - // models (nominal, may carry methods) keep the standard namespace so a - // mixed file still converts its facade-able models. - const facadeable = options.facade ? new Set(getFacadeableModelNames(targetContent)) : null - const resolveNames = facadeable ? modelNames.filter((n) => facadeable.has(n)) : modelNames - const standardNames = facadeable ? modelNames.filter((n) => !facadeable.has(n)) : [] - const block = resolveNames.length > 0 - ? resolver.generate(meta.filename, resolveNames, { - facade: options.facade ?? false, - make: options.facade || (options.make ?? false), - type: options.facade || options.type || options.make || false - }) - : "" - if (block === null) { - // Could not resolve (file outside program, etc.) — leave existing content. - return meta.existingContent - } - const standardBlock = standardNames - .map((n) => - `export namespace ${n} {\n export interface Encoded extends S.StructNestedEncoded {}\n}` - ) - .join("\n") - expectedContent = ["//", [block, standardBlock].filter((s) => s.length > 0).join("\n"), "//"].join("\n") - } else { - const them = modelNames.map((modelName) => [ - `export namespace ${modelName} {`, - ` export interface Encoded extends S.StructNestedEncoded {}`, - "}" - ]) - expectedContent = ["//", ...them.flat(), "//"].join("\n") - } - - // Fast path: whitespace-normalised comparison (avoids AST parse) - if (normaliseLines(meta.existingContent) === normaliseLines(expectedContent)) { - return meta.existingContent - } - return expectedContent - } catch (e) { - return ( - "/** Got exception: " - + ("stack" in (e as any) ? (e as any).stack : "") - + JSON.stringify(e) - + "*/" - ) - } -} diff --git a/packages/eslint-codegen-model/src/shared/codegen-block.ts b/packages/eslint-codegen-model/src/shared/codegen-block.ts deleted file mode 100644 index cc6e6335b..000000000 --- a/packages/eslint-codegen-model/src/shared/codegen-block.ts +++ /dev/null @@ -1,101 +0,0 @@ -import yaml from "js-yaml" -import { barrel } from "../presets/barrel.ts" -import { meta as metaPreset } from "../presets/meta.ts" -import { modelFacade } from "../presets/model-facade.ts" -import { model } from "../presets/model.ts" -import type { ModelTypeResolver } from "./type-resolver.ts" - -export type CodegenMeta = { - filename: string - existingContent: string -} - -export type BlockOptions = Record & { - preset: string -} - -export const blockRe = /^([ \t]*)\/\/ codegen:start[ \t]*(\{.*\})[ \t]*$\n?([\s\S]*?)^([ \t]*)\/\/ codegen:end[ \t]*$/gm - -const tsSourceRe = /\.[cm]?tsx?$/ -const jsExtRe = /(["'])(\.{1,2}\/[^"']+)\.js\1/g - -export function isRecord(input: unknown): input is Record { - return typeof input === "object" && input !== null && !Array.isArray(input) -} - -export function isBlockOptions(input: unknown): input is BlockOptions { - return isRecord(input) && typeof input["preset"] === "string" -} - -export function parseBlockOptions(input: string): BlockOptions { - const parsed = yaml.load(input) - if (!isBlockOptions(parsed)) { - throw new Error(`Invalid codegen options: ${input}`) - } - return parsed -} - -export function trimTrailingNewline(input: string): string { - return input.endsWith("\n") ? input.slice(0, -1) : input -} - -export function isTypeScriptSource(filePath: string): boolean { - return tsSourceRe.test(filePath) -} - -export function shouldStripJsExtensions(options: BlockOptions, filePath: string): boolean { - return options.preset === "barrel" - && options["jsExtensions"] === false - && isTypeScriptSource(filePath) -} - -export function normaliseGeneratedContent( - options: BlockOptions, - filePath: string, - content: string -): string { - if (!shouldStripJsExtensions(options, filePath)) { - return content - } - return content.replace(jsExtRe, "$1$2$1") -} - -export function indentBlock(content: string, indent: string): string { - if (indent.length === 0) { - return content - } - return content - .split("\n") - .map((line) => line.length === 0 ? line : `${indent}${line}`) - .join("\n") -} - -export type CodegenDefaults = Partial>> - -export function applyDefaults(options: BlockOptions, defaults?: CodegenDefaults): BlockOptions { - if (!defaults) return options - const presetDefaults = defaults[options.preset] - if (!presetDefaults) return options - return { ...presetDefaults, ...options, preset: options.preset } -} - -export function renderPreset( - options: BlockOptions, - meta: CodegenMeta, - fullSource?: string, - resolver?: ModelTypeResolver -): string { - const { preset, ...rest } = options - switch (preset) { - case "barrel": - return barrel({ meta, options: rest as Parameters[0]["options"] }, undefined) - case "meta": - return metaPreset({ meta, options: rest as Parameters[0]["options"] }, undefined) - case "model": - return model({ meta, options: rest as Parameters[0]["options"] }, fullSource, resolver) - case "modelFacade": - return modelFacade({ meta, options: rest as Parameters[0]["options"] }) - default: - throw new Error(`Unknown codegen preset: ${preset}`) - } -} diff --git a/packages/eslint-codegen-model/src/shared/native-type-resolver.ts b/packages/eslint-codegen-model/src/shared/native-type-resolver.ts deleted file mode 100644 index 833bdd45d..000000000 --- a/packages/eslint-codegen-model/src/shared/native-type-resolver.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Native (tsgo-backed) implementation of {@link ModelTypeResolver}. - * - * Where {@link createModelTypeResolver} drives the classic `typescript` Compiler - * API in-process, this resolver shells out to a forked `tsgolint` binary - * (`model-codegen` subcommand) that runs the type query on `typescript-go`. The - * binary builds the program once per invocation and answers a batch of model - * names over a one-shot JSON-on-stdio protocol. - * - * Selected via the CLI `--native` flag; the classic resolver remains the default. - * - * Vertical slice: only the `Encoded` member is ported. For any option that needs - * `Type`/`Make`/services/facade the resolver returns `null`, so the caller falls - * back to leaving the block untouched (same contract as "no resolver"). - */ -import { execFileSync } from "node:child_process" -import { createRequire } from "node:module" -import * as path from "node:path" -import type { ModelTypeResolver, ResolveOptions } from "./type-resolver.ts" - -const require_ = createRequire(import.meta.url) - -/** - * Resolve the forked tsgolint binary (the one that also backs `oxlint - * --type-aware`). Precedence: - * 1. `TSGOLINT_CODEGEN_BIN` (explicit override), - * 2. the `oxlint-tsgolint` package — repo-wide overridden to `repos/tsgolint-fork` - * via pnpm, whose shim extracts and returns the platform binary. - * - * Using the same package as oxlint guarantees one binary serves both the editor - * lint path (`headless`) and model codegen (`model-codegen`). - */ -function resolveBinary(): string { - const fromEnv = process.env["TSGOLINT_CODEGEN_BIN"] - if (fromEnv) return fromEnv - try { - const shimPath = require_.resolve("oxlint-tsgolint/bin/tsgolint.js") - const { ensureBinary } = require_(shimPath) as { ensureBinary: () => string } - return ensureBinary() - } catch (e) { - throw new Error( - `native model codegen: could not resolve the tsgolint-fork binary via oxlint-tsgolint (${ - (e as Error).message - }). Ensure the pnpm override is installed, or set TSGOLINT_CODEGEN_BIN.` - ) - } -} - -interface NativeResponse { - readonly ok: boolean - readonly blocks?: Record - readonly error?: string -} - -export function createNativeModelTypeResolver(args: { - readonly tsconfigPath: string - readonly binary?: string -}): ModelTypeResolver { - const tsconfig = path.resolve(args.tsconfigPath) - - // Resolve (and, on first use, fetch/extract) the binary lazily — only once a - // codegen block actually needs the type checker. Creating a resolver for a - // file that turns out to have no static/facade block costs nothing. Memoized. - let binary: string | undefined - const getBinary = () => (binary ??= args.binary ?? resolveBinary()) - - return { - generate(filename, modelNames, options: ResolveOptions): string | null { - const request = JSON.stringify({ - tsconfig, - fileName: path.resolve(filename), - models: modelNames, - options: { - type: options.type ?? false, - make: options.make ?? false, - facade: options.facade ?? false - } - }) - - let stdout: string - try { - stdout = execFileSync(getBinary(), ["model-codegen"], { - input: request, - encoding: "utf8", - maxBuffer: 64 * 1024 * 1024 - }) - } catch (e) { - const err = e as { stdout?: string } - // The binary still emits a JSON error payload on a non-zero exit. - stdout = err.stdout ?? "" - } - - let parsed: NativeResponse - try { - parsed = JSON.parse(stdout) as NativeResponse - } catch { - return null - } - if (!parsed.ok || !parsed.blocks) return null - - const blocks: Array = [] - for (const name of modelNames) { - const block = parsed.blocks[name] - if (block === undefined) return null - blocks.push(block) - } - return blocks.join("\n") - } - } -} diff --git a/packages/eslint-codegen-model/src/shared/type-resolver.ts b/packages/eslint-codegen-model/src/shared/type-resolver.ts deleted file mode 100644 index 6d8ab54e7..000000000 --- a/packages/eslint-codegen-model/src/shared/type-resolver.ts +++ /dev/null @@ -1,631 +0,0 @@ -/** - * Type-checker-backed resolver for the `model` preset's `static` mode. - * - * The text-only codegen emits `interface Encoded extends StructNestedEncoded` - * — a conditional type that TypeScript re-resolves on first property access, once per - * checker. For large schema trees that is a major instantiation cost (and, under the - * parallel checker pool, a depth-limit trigger). This resolver instead emits the - * *expanded* literal `Encoded` (and optionally `Type`) interface, with nested model - * fields referenced by name (`Item.Encoded`), which TypeScript resolves once and reuses. - * - * It uses the classic `typescript` Compiler API (loaded lazily) so the package's - * text-only path (oxlint rule) never needs a type checker. Only the CLI builds a program. - */ -import { createRequire } from "node:module" -import * as path from "node:path" - -const require_ = createRequire(import.meta.url) - -// `typescript` is an optional peer; only required for static mode (CLI). -type TS = typeof import("typescript") -type Type = import("typescript").Type -type Symbol_ = import("typescript").Symbol -type Node = import("typescript").Node -type TypeChecker = import("typescript").TypeChecker -type Program = import("typescript").Program - -export interface ResolveOptions { - /** Also emit a static `Type` interface (decoded side), enabling `S.OpaqueType`. */ - readonly type?: boolean - /** Also emit a static `Make` interface (make-input side), enabling `S.OpaqueShape`. Implies `type`. */ - readonly make?: boolean - /** Emit a shallow public `Schema` facade and top-level instance interface. Implies `type` and `make`. */ - readonly facade?: boolean -} - -export interface ModelTypeResolver { - /** - * Generate the `export namespace X { export interface Encoded {...} }` blocks for the - * given models in `filename`. Returns the joined block body, or `null` when the file - * is not part of the program / a model can't be resolved. - */ - generate(filename: string, modelNames: ReadonlyArray, options: ResolveOptions): string | null -} - -let tsModule: TS | undefined -function loadTs(): TS { - if (!tsModule) { - try { - tsModule = require_("typescript") as TS - } catch { - throw new Error("static model codegen requires the `typescript` package to be installed") - } - } - return tsModule -} - -function parseTsConfig( - ts: TS, - tsconfigPath: string -): { options: import("typescript").CompilerOptions; fileNames: Array } { - const read = ts.readConfigFile(tsconfigPath, ts.sys.readFile) - if (read.error) { - throw new Error( - `Failed to read tsconfig ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(read.error.messageText, "\n")}` - ) - } - const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, path.dirname(tsconfigPath)) - // `stableTypeOrdering` makes tsc order union/intersection constituents by a - // structural compare (the same order typescript-go uses), instead of by type - // id (creation/source order). Set it so the classic resolver's output matches - // the native (tsgo) resolver's — no divergence when switching backends. It is - // an internal compiler option (not in the public `CompilerOptions` type). - return { - options: { - ...parsed.options, - noEmit: true, - composite: false, - incremental: false, - skipLibCheck: true, - declaration: false, - stableTypeOrdering: true - } as import("typescript").CompilerOptions, - fileNames: parsed.fileNames - } -} - -export function createModelTypeResolver(args: { - readonly tsconfigPath: string - /** Extra files to include as program roots (the files being codegen'd). */ - readonly files?: ReadonlyArray -}): ModelTypeResolver { - const ts = loadTs() - const { fileNames, options } = parseTsConfig(ts, args.tsconfigPath) - const roots = Array.from(new Set([...fileNames, ...(args.files ?? [])].map((f) => path.resolve(f)))) - - let program: Program | undefined - let checker: TypeChecker | undefined - const getProgram = () => { - if (!program) { - program = ts.createProgram(roots, options) - checker = program.getTypeChecker() - } - return { program: program!, checker: checker! } - } - - return { - generate(filename, modelNames, opts) { - const { program, checker } = getProgram() - const sf = program.getSourceFile(path.resolve(filename)) - if (!sf) return null - - const wanted = new Set(modelNames) - // Name identifier of the schema that backs each model. The private `_X` - // (class `class _X extends S.Opaque(...)` OR const `const _X = S.Struct(...)`, - // the base-mode form) holds the real schema; the exported facade - // `X extends OpaqueFacade` / `extends __X` is - // self-referential and can't resolve `Encoded`/`Type`/`Make`. Prefer `_X`. - const schemaByName = new Map() - const privateNames = new Set() - const consider = (text: string, nameNode: import("typescript").Node) => { - if (text.startsWith("_") && !text.startsWith("__") && wanted.has(text.slice(1))) { - schemaByName.set(text.slice(1), nameNode) - privateNames.add(text.slice(1)) - } else if (wanted.has(text) && !privateNames.has(text)) { - schemaByName.set(text, nameNode) - } - } - sf.forEachChild((n) => { - if (ts.isClassDeclaration(n) && n.name) consider(n.name.text, n.name) - else if (ts.isVariableStatement(n)) { - for (const d of n.declarationList.declarations) { - if (ts.isIdentifier(d.name)) consider(d.name.text, d.name) - } - } - }) - if (schemaByName.size === 0) return null - - const printer = makePrinter(ts, checker, wanted) - const blocks: Array = [] - const facadeType = (body: string) => - body.replace(/\.Type\b/g, "").replace(/\n /g, "\n ").replace(/\n }$/, "\n}") - for (const name of modelNames) { - const nameNode = schemaByName.get(name) - if (!nameNode) return null - const sym = checker.getSymbolAtLocation(nameNode) - if (!sym) return null - const schemaType = checker.getTypeOfSymbolAtLocation(sym, nameNode) - const encoded = printer.member(schemaType, "Encoded", nameNode) - if (encoded === null) return null - const emitType = opts.facade || opts.type || opts.make - const emitMake = opts.facade || opts.make - const lines = opts.facade - ? [] - : [`export namespace ${name} {`, ` export interface Encoded ${encoded}`] - if (emitType) { - const typ = printer.member(schemaType, "Type", nameNode) - if (typ === null) return null - if (opts.facade) { - // Note: instance getters/methods are already included by `member(...)` - // above — an Opaque/Class `Self` is the class instance type, so the - // checker reports getters as properties of `Type`. No re-attach needed. - lines.push(`export interface ${name} ${facadeType(typ)}`) - lines.push(`export namespace ${name} {`) - lines.push(` export interface Encoded ${encoded}`) - } else { - lines.push(` export interface Type ${typ}`) - } - } - if (emitMake) { - const mk = printer.makeMember(schemaType, nameNode) - if (mk === null) return null - // A leading `= ` marks a type-alias emission (e.g. `{...} | void`, which - // an interface can't express); otherwise it's an interface body. - lines.push(mk.startsWith("=") ? ` export type Make ${mk}` : ` export interface Make ${mk}`) - } - if (opts.facade) { - const decodingServices = printer.serviceMember(schemaType, "DecodingServices", nameNode) - const encodingServices = printer.serviceMember(schemaType, "EncodingServices", nameNode) - if (decodingServices === null || encodingServices === null) return null - lines.push(` export type DecodingServices = ${decodingServices}`) - lines.push(` export type EncodingServices = ${encodingServices}`) - } - lines.push("}") - blocks.push(lines.join("\n")) - } - return blocks.join("\n") - } - } -} - -interface SchemaRef { - readonly sym: Symbol_ - /** The exact in-scope reference the author wrote (`X`, `ProcessingStates.X`). */ - readonly name: string -} -interface FieldRef { - readonly refs: ReadonlyArray - wrapper: "" | "nonempty" | "array" - nullable: boolean -} - -function makePrinter(ts: TS, checker: TypeChecker, modelNames: ReadonlySet) { - // Note: deliberately NOT using `InTypeAlias` — that flag expands the alias being - // printed (turning `NonEmptyString255` into `string & ...Brand`). Without it, - // typeToString prefers the named alias symbol when one exists. - const FF = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseFullyQualifiedType - // A field whose printed expansion is this big (or multi-line) is worth replacing - // with a const reference, when one is available. - const FIELD_REDIRECT_LIMIT = 200 - - const skipAlias = (s: Symbol_): Symbol_ => (s.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(s) : s - - // Prefix a model symbol with its enclosing namespace(s): a model `Label` nested in - // `namespace ProcessingStates` is referenced as `ProcessingStates.Label`. Walks up - // namespace/module parents, stopping at the source-file module (a quoted path). - function qualify(sym: Symbol_): string { - let name = sym.name - let p = (sym as Symbol_ & { parent?: Symbol_ }).parent - while (p) { - if ((p.flags & (ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule)) === 0) break - if (!p.name || p.name.startsWith("\"") || p.name.startsWith("'")) break - name = `${p.name}.${name}` - p = (p as Symbol_ & { parent?: Symbol_ }).parent - } - return name - } - - // A symbol whose `X.Encoded`/`X.Type` resolves in this file: one of this file's - // models, or any imported class/interface/namespace. - function isModelParent(p: Symbol_): boolean { - if (modelNames.has(p.name)) return true - return (p.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface | ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule)) !== 0 - } - - // If `t` is a model's `Encoded` namespace interface -> "Ns.Name.Encoded". - function modelEncodedName(t: Type): string | null { - const sym = (t.aliasSymbol ?? t.symbol) as (Symbol_ & { parent?: Symbol_ }) | undefined - if (!sym || sym.name !== "Encoded" || !sym.parent) return null - if (isModelParent(sym.parent)) return `${qualify(sym.parent)}.Encoded` - return null - } - - // If `t` is a model's instance type -> "Ns.Name.Type". Two shapes occur: - // - Self = the class (before/without the self-rewrite): symbol name === ModelName. - // - Self = `X.Type` (after the self-rewrite): symbol is the `Type` interface, - // name === "Type", parent === ModelName. - function modelTypeName(t: Type): string | null { - const sym = (t.aliasSymbol ?? t.symbol) as (Symbol_ & { parent?: Symbol_ }) | undefined - if (!sym) return null - if (sym.name === "Type" && sym.parent && isModelParent(sym.parent)) return `${qualify(sym.parent)}.Type` - if (modelNames.has(sym.name)) return `${sym.name}.Type` - return null - } - - // An anonymous object literal type (inline struct), as opposed to a named - // interface/class (Date, branded scalars, library types) which we keep by name. - function isAnonymousObject(t: Type): boolean { - if ((t.flags & ts.TypeFlags.Object) === 0) return false - if ((t as import("typescript").ObjectType).objectFlags & ts.ObjectFlags.Anonymous) { - const sym = t.symbol as Symbol_ | undefined - // TypeLiteral / ObjectLiteral symbols are inline; a named interface is not. - return !sym || (sym.flags & (ts.SymbolFlags.TypeLiteral | ts.SymbolFlags.ObjectLiteral)) !== 0 - } - return false - } - - // Wrap a printed element in parens when used as an array/tuple element and it - // contains a top-level union/intersection (precedence). - function asElement(s: string): string { - // Parenthesize unions/intersections AND `readonly`-prefixed elements (nested - // arrays/tuples) so `ReadonlyArray` prints as - // `readonly (readonly [..])[]`, not the invalid `readonly readonly [..][]`. - return /[|&]/.test(s) || s.startsWith("readonly ") ? `(${s})` : s - } - // Parenthesize a reference used as an array element where precedence matters. - const parenElem = (s: string): string => - /[|&]/.test(s) || s.startsWith("typeof ") || s.startsWith("readonly ") ? `(${s})` : s - - // A printed field type large enough to be worth replacing with a const reference: - // a multi-line object expansion, or a long single line (a huge union that would - // otherwise be one formatter-crashing line). - const wouldExpandBig = (printed: string): boolean => printed.includes("\n") || printed.length > FIELD_REDIRECT_LIMIT - - // --- schema-AST capture: recover named references for a field, so a big expansion - // can be replaced by `X.Encoded` / `typeof X.Encoded` / a union/wrapper of those --- - - const PASSTHROUGH = new Set(["withConstructorDefault", "withDefault", "withDecodingDefault", "annotations"]) - const isPassthrough = (n: Node | undefined): boolean => - !!n && ts.isIdentifier(n) && PASSTHROUGH.has(n.text) - - function wrapperKind(name: string): "" | "nonempty" | "array" | "nullor" { - switch (name) { - case "NonEmptyArray": - case "NonEmptyReadonlyArray": - case "NonEmptyChunk": - return "nonempty" - case "Array": - case "ReadonlyArray": - case "Chunk": - return "array" - case "NullOr": - case "NullishOr": - return "nullor" - } - return "" - } - - const calleeName = (e: Node | undefined): string => - !e ? "" : ts.isIdentifier(e) ? e.text : ts.isPropertyAccessExpression(e) ? e.name.text : "" - - // The exact source reference (`X`, `ProcessingStates.X`) of an identifier / property - // access — guaranteed resolvable in the file we emit into. - function nodeRefName(node: Node): string { - if (ts.isIdentifier(node)) return node.text - if (ts.isPropertyAccessExpression(node)) { - const base = nodeRefName(node.expression) - return base ? `${base}.${node.name.text}` : "" - } - return "" - } - - // Resolve an uppercase-named schema const a node refers to; null for method names - // (`withConstructorDefault`) or non-consts (`Struct`). - function resolveConst(node: Node): SchemaRef | null { - let s = checker.getSymbolAtLocation(node) - if (!s) return null - s = skipAlias(s) - if (!s.name || s.name[0]! < "A" || s.name[0]! > "Z") return null - const name = nodeRefName(node) - return name ? { sym: s, name } : null - } - - // Recover named references from a field value: a bare/namespaced identifier, a - // recognised wrapper (`NonEmptyArray(X)`, `NullOr(X)`), a `Union([A, B, …])`, or any - // of those behind a type-preserving accessor (`.withConstructorDefault`). - function fieldRefOf(val: Node): FieldRef | null { - if (ts.isIdentifier(val) || ts.isPropertyAccessExpression(val)) { - const r = resolveConst(val) - if (r) return { refs: [r], wrapper: "", nullable: false } - if (ts.isPropertyAccessExpression(val) && isPassthrough(val.name)) return fieldRefOf(val.expression) - return null - } - if (ts.isCallExpression(val)) { - const callee = val.expression - const name = calleeName(callee) - const args = val.arguments - const wk = wrapperKind(name) - if (wk === "nonempty" || wk === "array") { - if (args.length === 1) { - const inner = fieldRefOf(args[0]!) - if (inner && inner.wrapper === "" && !inner.nullable) { - inner.wrapper = wk - return inner - } - } - return null - } - if (wk === "nullor") { - if (args.length === 1) { - const inner = fieldRefOf(args[0]!) - if (inner && !inner.nullable) { - inner.nullable = true - return inner - } - } - return null - } - const arg0 = args[0] - if (name === "Union" && args.length === 1 && arg0 && ts.isArrayLiteralExpression(arg0)) { - const refs: Array = [] - for (const el of arg0.elements) { - const r = resolveConst(el) - if (!r) return null // a non-const member -> can't name them all - refs.push(r) - } - return refs.length > 0 ? { refs, wrapper: "", nullable: false } : null - } - // fluent method on a schema value: `X.withConstructorDefault(...)` -> receiver - if (ts.isPropertyAccessExpression(callee) && isPassthrough(callee.name)) return fieldRefOf(callee.expression) - } - return null - } - - // Map each field of the model's backing schema to its named reference(s), by walking - // the `_X` declaration's source AST. Follows object spreads (`...projectedFields`) so - // fields merged in by spread are captured too. Robust to `.pipe(encodeKeys/…)`. - function structFieldSymbols(nameNode: Node): Map { - const out = new Map() - const nsym = checker.getSymbolAtLocation(nameNode) - const decl = nsym?.valueDeclaration - if (!decl || !ts.isVariableDeclaration(decl) || !decl.initializer) return out - const visited = new Set() - const walk = (n: Node): void => { - n.forEachChild((c) => { - if (ts.isPropertyAssignment(c)) { - const pn = c.name - const fn = ts.isIdentifier(pn) || ts.isStringLiteral(pn) || ts.isNumericLiteral(pn) ? pn.text : undefined - if (fn !== undefined && c.initializer && !out.has(fn)) { - const ref = fieldRefOf(c.initializer) - if (ref) out.set(fn, ref) - } - } else if (ts.isSpreadAssignment(c) && ts.isIdentifier(c.expression)) { - let s = checker.getSymbolAtLocation(c.expression) - if (s) { - s = skipAlias(s) - if (!visited.has(s) && s.valueDeclaration && ts.isVariableDeclaration(s.valueDeclaration) && s.valueDeclaration.initializer) { - visited.add(s) - walk(s.valueDeclaration.initializer) - } - } - } - walk(c) - return undefined - }) - } - walk(decl.initializer) - return out - } - - // The const-reference fallback chain for one ref (per `key`): - // 1. `X.Encoded` / `X` / `X.Make` when the declaration exists (namespace `Encoded`, - // a `type X` alias / class, namespace `Make`), - // 2. else `typeof X.Encoded` / `typeof X.Type` / `typeof X["~type.make.in"]`. - function constLeaf(r: SchemaRef, key: "Encoded" | "Type" | "Make"): string { - const { name, sym } = r - const exports = (sym as Symbol_ & { exports?: import("typescript").SymbolTable }).exports - switch (key) { - case "Encoded": - if (exports?.has("Encoded" as import("typescript").__String)) return `${name}.Encoded` - return `typeof ${name}.Encoded` - case "Type": - // A class/interface/alias name already denotes the decoded type directly. - if ((sym.flags & (ts.SymbolFlags.TypeAlias | ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) !== 0) return name - return `typeof ${name}.Type` - case "Make": - if (exports?.has("Make" as import("typescript").__String)) return `${name}.Make` - return `typeof ${name}["~type.make.in"]` - } - } - - // Build the reference type for a field backed by named const(s), rebuilding the shape - // (`null | A.Encoded | typeof B.Encoded`, `readonly [X.Encoded, ...]`) around the - // leaves. Returns "" when any leaf isn't nameable. - function fieldConstRef(ref: FieldRef | undefined, key: "Encoded" | "Type" | "Make"): string { - if (!ref || ref.refs.length === 0) return "" - const parts: Array = [] - for (const r of ref.refs) { - const leaf = constLeaf(r, key) - if (!leaf) return "" - parts.push(leaf) - } - let core = parts.join(" | ") - if (ref.wrapper === "nonempty") core = `readonly [${core}, ...${parenElem(core)}[]]` - else if (ref.wrapper === "array") core = `readonly ${parenElem(core)}[]` - if (ref.nullable) core = `null | ${core}` - return core - } - - function print(t: Type, atNode: Node, side: "Encoded" | "Type"): string { - if (side === "Encoded") { - const mn = modelEncodedName(t) - if (mn) return mn - } else { - const mn = modelTypeName(t) - if (mn) return mn - } - // union - if (t.isUnion()) return t.types.map((x) => print(x, atNode, side)).join(" | ") - // tuple (e.g. NonEmptyArray -> readonly [E, ...(E)[]]) - if (checker.isTupleType(t)) { - const target = (t as import("typescript").TupleTypeReference).target - const args = checker.getTypeArguments(t as import("typescript").TypeReference) - const parts = args.map((a, i) => { - const flag = target.elementFlags[i]! - const isRest = (flag & ts.ElementFlags.Rest) !== 0 - const isOpt = (flag & ts.ElementFlags.Optional) !== 0 - const el = print(a, atNode, side) - return isRest ? `...${asElement(el)}[]` : `${el}${isOpt ? "?" : ""}` - }) - return `readonly [${parts.join(", ")}]` - } - // array - if (checker.isArrayType(t)) { - const el = print(checker.getTypeArguments(t as import("typescript").TypeReference)[0]!, atNode, side) - return `readonly ${asElement(el)}[]` - } - // anonymous inline object -> expand structurally; named objects (Date, etc.) by name. - // Multi-line: a deeply-nested inline object on one line can reach 50KB+ and crash the - // formatter; the whitespace-insensitive codegen compare keeps this stable vs dprint. - if (isAnonymousObject(t)) { - const props = t.getProperties() - if (props.length > 0) return expandObject(props, atNode, side) - } - // primitives, literals, branded scalars, named library types, etc. - const printed = checker.typeToString(t, atNode, FF) - // Safety net: a large object dump that `isAnonymousObject` didn't catch — expand it - // structurally so it's multi-line and the formatter can't OOM on a giant single line. - if (printed.length > FIELD_REDIRECT_LIMIT && (t.flags & ts.TypeFlags.Object) !== 0) { - const props = t.getProperties() - if (props.length > 0) return expandObject(props, atNode, side) - } - // On the Type side a branded scalar prints as `string & Ns.FooBrand`; prefer the - // schema's companion type alias `Ns.Foo` (nominal, cheaper, what authors wrote). - if (side === "Type") return namedScalar(printed) - return printed - } - - function expandObject(props: ReadonlyArray, atNode: Node, side: "Encoded" | "Type"): string { - const parts = props.map((p) => { - const pt = checker.getTypeOfSymbolAtLocation(p, atNode) - const opt = (p.flags & ts.SymbolFlags.Optional) !== 0 ? "?" : "" - return `readonly ${propKey(p.name)}${opt}: ${print(pt, atNode, side)}` - }) - return `{\n${parts.join("\n")}\n}` - } - - // ` & Brand` -> `` (the schema's companion scalar type). - function namedScalar(s: string): string { - const m = /^[\w.[\]"'| ]+ & ([\w.$]+)Brand$/.exec(s) - return m ? m[1]! : s - } - - return { - /** - * Non-static instance getters/methods declared on the model class body. They - * live on the runtime `_X` (inherited by the facade `X`) but are not schema - * fields, so they must be re-attached to the generated `Self` interface. - */ - instanceMembers(cls: import("typescript").ClassDeclaration): Array { - const out: Array = [] - for (const m of cls.members) { - const isStatic = (ts.getCombinedModifierFlags(m) & ts.ModifierFlags.Static) !== 0 - if (isStatic || !m.name || !ts.isIdentifier(m.name)) continue - const memberName = m.name.text - if (ts.isGetAccessorDeclaration(m)) { - const t = checker.getTypeAtLocation(m) - out.push(`readonly ${memberName}: ${checker.typeToString(t, m, FF)}`) - } else if (ts.isMethodDeclaration(m)) { - const t = checker.getTypeAtLocation(m) - out.push(`readonly ${memberName}: ${checker.typeToString(t, m, FF)}`) - } - } - return out - }, - - /** Expand the top-level `Encoded`/`Type` interface of `schemaType` one level, nested by name. */ - member(schemaType: Type, key: "Encoded" | "Type", atNode: Node): string | null { - const memberSym = checker.getPropertyOfType(schemaType, key) - if (!memberSym) return null - const memberType = checker.getTypeOfSymbolAtLocation(memberSym, atNode) - const props = memberType.getProperties() - if (props.length === 0) { - // Not an expandable object (e.g. opaque already); fall back to a printed reference. - return `extends ${checker.typeToString(memberType, atNode, FF)} {}` - } - const fieldSyms = structFieldSymbols(atNode) - const body = props - .map((p) => { - const pt = checker.getTypeOfSymbolAtLocation(p, atNode) - const opt = (p.flags & ts.SymbolFlags.Optional) !== 0 ? "?" : "" - // A field that expands big AND is backed by named schema const(s) -> reference - // them (`X.Encoded` / `typeof X.Encoded` / wrapper shape) instead of expanding. - let val = print(pt, atNode, key) - if (wouldExpandBig(val)) { - const ref = fieldConstRef(fieldSyms.get(p.name), key) - if (ref) val = ref - } - return ` readonly ${propKey(p.name)}${opt}: ${val}` - }) - .join("\n") - return `{\n${body}\n }` - }, - - /** - * Expand the `make`-input interface (`~type.make.in`). Keys + optionality come from the - * make-input member (so defaulted fields and `_tag` are optional); each value is the - * Type-side shape with nested model refs rewritten `.Type` -> `.Make`. - */ - makeMember(schemaType: Type, atNode: Node): string | null { - const makeSym = checker.getPropertyOfType(schemaType, "~type.make.in") - const typeSym = checker.getPropertyOfType(schemaType, "Type") - if (!makeSym || !typeSym) return null - const rawMakeType = checker.getTypeOfSymbolAtLocation(makeSym, atNode) - const typeType = checker.getTypeOfSymbolAtLocation(typeSym, atNode) - // `withConstructorDefault` makes the make-input `void | { ...all optional }`. - // The `void` is NOT cosmetic: effect-app's `make`/`makeEffect` key off it to - // make the input argument optional (a no-arg call). So we must preserve it. - // A union has no own properties and `interface Make extends void | {...}` is a - // syntax error, so when `void`/`undefined` is present we emit a TYPE ALIAS - // (`export type Make = { ... } | void`) — signalled by a leading `= `. - const isVoidish = (t: Type) => (t.flags & (ts.TypeFlags.Void | ts.TypeFlags.Undefined)) !== 0 - const hasVoid = rawMakeType.isUnion() && rawMakeType.types.some(isVoidish) - const makeType = rawMakeType.isUnion() - ? (rawMakeType.types.find((t) => t.getProperties().length > 0) ?? rawMakeType) - : rawMakeType - const makeProps = makeType.getProperties() - if (makeProps.length === 0) { - const printed = checker.typeToString(rawMakeType, atNode, FF) - return `= ${printed}` - } - const typeByName = new Map(typeType.getProperties().map((p) => [p.name, p] as const)) - const fieldSyms = structFieldSymbols(atNode) - const body = makeProps - .map((p) => { - const opt = (p.flags & ts.SymbolFlags.Optional) !== 0 ? "?" : "" - const source = typeByName.get(p.name) ?? p - const printed = print(checker.getTypeOfSymbolAtLocation(source, atNode), atNode, "Type") - // nested model `Foo.Type` becomes `Foo.Make`; scalars / Date / primitives untouched. - let value = printed.replace(/\.Type\b/g, ".Make") - if (wouldExpandBig(value)) { - const ref = fieldConstRef(fieldSyms.get(p.name), "Make") - if (ref) value = ref - } - return ` readonly ${propKey(p.name)}${opt}: ${value}` - }) - .join("\n") - // Leading `= ` marks a type-alias emission (model.ts emits `export type Make = ...`). - return hasVoid ? `= {\n${body}\n } | void` : `{\n${body}\n }` - }, - - serviceMember(schemaType: Type, key: "DecodingServices" | "EncodingServices", atNode: Node): string | null { - const memberSym = checker.getPropertyOfType(schemaType, key) - if (!memberSym) return null - return checker.typeToString(checker.getTypeOfSymbolAtLocation(memberSym, atNode), atNode, FF) - } - } - - function propKey(name: string): string { - return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name) - } -} diff --git a/packages/eslint-codegen-model/tsconfig.json b/packages/eslint-codegen-model/tsconfig.json deleted file mode 100644 index 83420995f..000000000 --- a/packages/eslint-codegen-model/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "tsBuildInfoFile": "./dist/.tsbuildinfo", - "rootDir": "./src", - "target": "ES2018", - "emitDecoratorMetadata": true, - "experimentalDecorators": true - }, - "include": [ - "src/**/*" - ] -} diff --git a/packages/eslint-shared-config/CHANGELOG.md b/packages/eslint-shared-config/CHANGELOG.md deleted file mode 100644 index 83f123d9b..000000000 --- a/packages/eslint-shared-config/CHANGELOG.md +++ /dev/null @@ -1,535 +0,0 @@ -# @effect-app/eslint-shared-config - -## 0.6.0-beta.36 - -### Patch Changes - -- c63c0af: Run configured Effect diagnostics through Oxlint's type-aware lint integration. - - @effect-app/eslint-codegen-model@2.0.0-beta.27 - -## 0.6.0-beta.35 - -### Patch Changes - -- Updated dependencies [a354345] -- Updated dependencies [a69da09] - - @effect-app/eslint-codegen-model@2.0.0-beta.27 - -## 0.6.0-beta.34 - -### Patch Changes - -- Updated dependencies [e71b67a] - - @effect-app/eslint-codegen-model@2.0.0-beta.26 - -## 0.6.0-beta.33 - -### Patch Changes - -- Updated dependencies [21ac90a] -- Updated dependencies [d605f3d] -- Updated dependencies [82655fa] - - @effect-app/eslint-codegen-model@2.0.0-beta.25 - -## 0.6.0-beta.32 - -### Patch Changes - -- Updated dependencies [50b022e] -- Updated dependencies [50b022e] - - @effect-app/eslint-codegen-model@2.0.0-beta.24 - -## 0.6.0-beta.31 - -### Patch Changes - -- 55c6572: update packages -- Updated dependencies [55c6572] - - @effect-app/eslint-codegen-model@2.0.0-beta.23 - -## 0.6.0-beta.30 - -### Patch Changes - -- Updated dependencies [b2e0f62] - - @effect-app/eslint-codegen-model@2.0.0-beta.22 - -## 0.6.0-beta.29 - -### Patch Changes - -- 22be885: Add `stripSuffixes` option to the meta codegen preset and configure `.Queries` stripping in the shared oxlint config. -- Updated dependencies [22be885] - - @effect-app/eslint-codegen-model@2.0.0-beta.21 - -## 0.6.0-beta.28 - -### Patch Changes - -- bogus -- Updated dependencies - - @effect-app/eslint-codegen-model@2.0.0-beta.20 - -## 0.6.0-beta.27 - -### Patch Changes - -- b8b9080: update packages -- Updated dependencies [b8b9080] - - @effect-app/eslint-codegen-model@2.0.0-beta.19 - -## 0.6.0-beta.26 - -### Patch Changes - -- Updated dependencies [34ab818] -- Updated dependencies [ccd4845] - - @effect-app/eslint-codegen-model@2.0.0-beta.18 - -## 0.6.0-beta.25 - -### Patch Changes - -- Updated dependencies [f04327a] - - @effect-app/eslint-codegen-model@2.0.0-beta.17 - -## 0.6.0-beta.24 - -### Patch Changes - -- c991be1: update packages -- Updated dependencies [c991be1] - - @effect-app/eslint-codegen-model@2.0.0-beta.15 - -## 0.6.0-beta.23 - -### Patch Changes - -- Updated dependencies [830e1ce] - - @effect-app/eslint-codegen-model@2.0.0-beta.14 - -## 0.6.0-beta.22 - -### Patch Changes - -- b3ea988: update dprint plugins - - @effect-app/eslint-codegen-model@2.0.0-beta.13 - -## 0.6.0-beta.21 - -### Patch Changes - -- dba5779: namespaces - - @effect-app/eslint-codegen-model@2.0.0-beta.13 - -## 0.6.0-beta.20 - -### Patch Changes - -- Updated dependencies [8c753cb] - - @effect-app/eslint-codegen-model@2.0.0-beta.13 - -## 0.6.0-beta.19 - -### Minor Changes - -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. - -### Patch Changes - -- 50ce7e6: Cleanup after tsgolint + oxlint-codegen-plugin migration: - - Wire `@effect-app/eslint-codegen-model/oxlint` via `jsPlugins` object form (`{ name: "codegen", specifier: ... }`) so the `codegen/codegen` rule key resolves. - - Drop `eslint-plugin-codegen` dep, patch, and `augmentedConfig` helper — codegen now runs through oxlint. - - Break cyclic workspace dep between `eslint-codegen-model` and `eslint-shared-config`; remove dead `eslint.config.mjs` from `eslint-codegen-model`. - - Switch `@effect-app/vue` to oxlint-only (no `.vue` files in `src`); drop its ESLint config and `eslint-shared-config` devDep. - - Restore `@typescript-eslint` plugin and rules in shared `baseConfig` so inline `eslint-disable @typescript-eslint/...` directives resolve in `@effect-app/vue-components` (the only remaining ESLint consumer, for `.vue` files). - - Add `globals.browser` to `vueConfig` so browser globals (`window`, `console`, `URL`, etc.) resolve. -- Updated dependencies [50ce7e6] - - @effect-app/eslint-codegen-model@2.0.0-beta.12 - -## 0.6.0-beta.18 - -### Patch Changes - -- 16b1cf9: update oxlint rule - - @effect-app/eslint-codegen-model@2.0.0-beta.11 - -## 0.6.0-beta.17 - -### Patch Changes - -- c1e73de: -- Updated dependencies [c1e73de] - - @effect-app/eslint-codegen-model@2.0.0-beta.11 - -## 0.6.0-beta.16 - -### Patch Changes - -- Updated dependencies [6fff09c] - - @effect-app/eslint-codegen-model@2.0.0-beta.10 - -## 0.6.0-beta.15 - -### Patch Changes - -- 16bbd9c: ship shared dprint config and wire oxlint and dprint into package lint scripts - - @effect-app/eslint-codegen-model@2.0.0-beta.9 - -## 0.6.0-beta.14 - -### Patch Changes - -- f58e58a: Add shared oxlintrc.json base config. Consumers extend via `"extends": ["./node_modules/@effect-app/eslint-shared-config/src/oxlintrc.json"]`. Note: oxlint does not merge `ignorePatterns` — repeat the base patterns and add project-specific ones. - - @effect-app/eslint-codegen-model@2.0.0-beta.9 - -## 0.6.0-beta.13 - -### Patch Changes - -- Updated dependencies [25008fb] -- Updated dependencies [77efa95] - - @effect-app/eslint-codegen-model@2.0.0-beta.9 - -## 0.6.0-beta.12 - -### Patch Changes - -- Updated dependencies [025de47] - - @effect-app/eslint-codegen-model@2.0.0-beta.8 - -## 0.6.0-beta.11 - -### Minor Changes - -- 4b91f31: Add oxlint integration: run oxlint before ESLint, with `eslint-plugin-oxlint` disabling ESLint rules already covered by oxlint. Add repo-root `.oxlintrc.json` with shared rule config. Update `lint`/`lint-fix` scripts in cli, effect-app, and infra packages. - -### Patch Changes - -- c3299f7: update packages -- Updated dependencies [c3299f7] - - @effect-app/eslint-codegen-model@2.0.0-beta.7 - -## 0.6.0-beta.10 - -### Minor Changes - -- af43f2f: Add `no-await-effect` lint rule that errors when awaiting Effect, Option, Result, or Fiber values from the effect library. Enabled when type-checked linting is active (`ESLINT_TS=1`). - -### Patch Changes - -- @effect-app/eslint-codegen-model@2.0.0-beta.6 - -## 0.5.7-beta.9 - -### Patch Changes - -- Updated dependencies [3b27909] - - @effect-app/eslint-codegen-model@2.0.0-beta.6 - -## 0.5.7-beta.8 - -### Patch Changes - -- Updated dependencies [3613e87] - - @effect-app/eslint-codegen-model@2.0.0-beta.5 - -## 0.5.7-beta.7 - -### Patch Changes - -- cec026d: update packages -- Updated dependencies [cec026d] - - @effect-app/eslint-codegen-model@2.0.0-beta.4 - -## 0.5.7-beta.6 - -### Patch Changes - -- 784c732: bs - - @effect-app/eslint-codegen-model@2.0.0-beta.3 - -## 0.5.7-beta.5 - -### Patch Changes - -- 68c3ba6: fml - - @effect-app/eslint-codegen-model@2.0.0-beta.3 - -## 0.5.7-beta.5 - -### Patch Changes - -- c82d746: dprint bs - - @effect-app/eslint-codegen-model@2.0.0-beta.3 - -## 0.5.7-beta.4 - -### Patch Changes - -- 10b55ff: update packages -- Updated dependencies [10b55ff] - - @effect-app/eslint-codegen-model@2.0.0-beta.3 - -## 0.5.7-beta.3 - -### Patch Changes - -- c9caa37: pin dprint/formatter - - @effect-app/eslint-codegen-model@2.0.0-beta.2 - -## 0.5.7-beta.2 - -### Patch Changes - -- 01c70d0: update all teh tings -- Updated dependencies [01c70d0] - - @effect-app/eslint-codegen-model@2.0.0-beta.2 - -## 0.5.7-beta.1 - -### Patch Changes - -- 64786af: Beta25 -- Updated dependencies [64786af] - - @effect-app/eslint-codegen-model@2.0.0-beta.1 - -## 0.5.7-beta.0 - -### Patch Changes - -- Updated dependencies [880df28] - - @effect-app/eslint-codegen-model@2.0.0-beta.0 - -## 0.5.6 - -### Patch Changes - -- dcb2df4: bump down formatter - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.5 - -### Patch Changes - -- 43334b4: try again -- d315086: patch dprint - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.2 - -### Patch Changes - -- 344f332: bump to latest version again - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.3 - -### Patch Changes - -- 8adb2df: downgrade dprint plugin - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.2 - -### Patch Changes - -- 6a8546b: bump dprint plugin - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.1 - -### Patch Changes - -- 2c0dc9d: patch fix? - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.5.0 - -### Minor Changes - -- 1a102e2: update packages - -### Patch Changes - -- Updated dependencies [1a102e2] - - @effect-app/eslint-codegen-model@1.47.0 - -## 0.4.1 - -### Patch Changes - -- 358782e: update packages -- Updated dependencies [358782e] - - @effect-app/eslint-codegen-model@1.46.1 - -## 0.4.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -### Patch Changes - -- Updated dependencies [07f1de5] - - @effect-app/eslint-codegen-model@1.46.0 - -## 0.3.4 - -### Patch Changes - -- eb86b8e: oops - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.3.3 - -### Patch Changes - -- 46faf2c: update dprint plugin, removing custom hack - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.3.2 - -### Patch Changes - -- m - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.3.2 - -### Patch Changes - -- 6592c9e: improve: turn floating promises to error - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.3.1 - -### Patch Changes - -- 587bc7b: bump patch - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.3.0 - -### Minor Changes - -- b8f5e30: update packages - -### Patch Changes - -- Updated dependencies [b8f5e30] - - @effect-app/eslint-codegen-model@1.45.0 - -## 0.2.1 - -### Patch Changes - -- cec6576: set @typescript-eslint/unbound-method as error (note: it's a type aware rule) - - @effect-app/eslint-codegen-model@1.44.0 - -## 0.2.0 - -### Minor Changes - -- 6d8c512: update packages - -### Patch Changes - -- Updated dependencies [6d8c512] - - @effect-app/eslint-codegen-model@1.44.0 - -## 0.1.1 - -### Patch Changes - -- f5358fe: update plugin - - @effect-app/eslint-codegen-model@1.43.0 - -## 0.1.0 - -### Minor Changes - -- 3d49d81: update packages - -### Patch Changes - -- Updated dependencies [3d49d81] - - @effect-app/eslint-codegen-model@1.43.0 - -## 0.0.13 - -### Patch Changes - -- e366964: 120 line width - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.12 - -### Patch Changes - -- 885a76f: enableTS is now defined again - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.11 - -### Patch Changes - -- 3454a27: enable projectService only when ENV var is set - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.10 - -### Patch Changes - -- 01e379a: always set tsconfigRootDir - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.9 - -### Patch Changes - -- d6a394c: fix jsonc-parser - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.8 - -### Patch Changes - -- f503246: fix missing dep - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.7 - -### Patch Changes - -- 569fec4: trying to have an autonomous shared-config eslint - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.6 - -### Patch Changes - -- 41ba3e9: fix publication - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.5 - -### Patch Changes - -- 9c1de6c: trying to internally patch benten+2 - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.4 - -### Patch Changes - -- f35da3c: porting more to eslint shared config package - - @effect-app/eslint-codegen-model@1.42.3 - -## 0.0.2 - -### Patch Changes - -- 8096576: new eslint shared config package -- Updated dependencies [8096576] - - @effect-app/eslint-codegen-model@1.42.3 diff --git a/packages/eslint-shared-config/package.json b/packages/eslint-shared-config/package.json deleted file mode 100644 index c11b19fec..000000000 --- a/packages/eslint-shared-config/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "@effect-app/eslint-shared-config", - "version": "0.6.0-beta.36", - "description": "Shared flat ESLint base & vue configs for effect-app monorepo", - "license": "MIT", - "private": false, - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/eslint-shared-config" - }, - "sideEffects": false, - "scripts": { - "watch": "pnpm build -w", - "build": "echo 'Nothing to build (config only)'", - "pub": "pnpm prepublish && npm publish --access public", - "prepublish": "echo 'No build step for config package'", - "postpublish": "echo 'Published @effect-app/eslint-shared-config'", - "prepack": "echo 'ready'" - }, - "exports": { - "./eslint.base.config": { - "default": "./src/eslint.base.config.mjs" - }, - "./eslint.vue.config": { - "default": "./src/eslint.vue.config.mjs" - }, - "./oxlintrc.json": "./src/oxlintrc.json", - "./dprint.jsonc": "./src/dprint.jsonc" - }, - "dependencies": { - "@effect-app/eslint-codegen-model": "workspace:*", - "oxlint": "1.67.0", - "oxlint-tsgolint": "0.23.0", - "@eslint/js": "10.0.1", - "@eslint/eslintrc": "3.3.5", - "eslint-plugin-import": "2.32.0", - "eslint-plugin-sort-destructure-keys": "3.0.0", - "eslint-plugin-unused-imports": "4.4.1", - "eslint-plugin-formatjs": "6.4.13", - "eslint-plugin-vue": "10.9.1", - "vue-eslint-parser": "10.4.0", - "@typescript-eslint/parser": "8.60.0", - "@typescript-eslint/eslint-plugin": "8.60.0", - "globals": "17.6.0", - "dprint-plugin-malva": "0.16.0", - "dprint-plugin-markup": "0.27.2", - "dprint-plugin-yaml": "0.6.0", - "@dprint/typescript": "0.96.1", - "@dprint/formatter": "~0.5.1", - "jsonc-parser": "3.3.1" - }, - "devDependencies": {}, - "peerDependencies": { - "eslint": "^10.4.1", - "typescript": "~6.0.3" - } -} diff --git a/packages/eslint-shared-config/src/dprint.jsonc b/packages/eslint-shared-config/src/dprint.jsonc deleted file mode 100644 index 6551ad162..000000000 --- a/packages/eslint-shared-config/src/dprint.jsonc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "typescript": { - "indentWidth": 2, - "semiColons": "asi", - "quoteStyle": "alwaysDouble", - "trailingCommas": "never", - "arrowFunction.useParentheses": "force", - "memberExpression.linePerExpression": true, - "binaryExpression.linePerExpression": true, - "importDeclaration.forceSingleLine": true, - "exportDeclaration.forceSingleLine": true, - "lineWidth": 120, - }, - "json": {}, - "markdown": {}, - "toml": {}, - "malva": {}, - "markup": { - "printWidth": 120, - }, - "yaml": {}, - // excludes overwrites (not merges) when extended — consumers must repeat these plus their own entries. - "excludes": [ - "**/node_modules", - "**/*-lock.json", - ], - "plugins": [ - "https://plugins.dprint.dev/typescript-0.95.15.wasm", - "https://plugins.dprint.dev/json-0.21.3.wasm", - "https://plugins.dprint.dev/markdown-0.21.1.wasm", - "https://plugins.dprint.dev/toml-0.7.0.wasm", - "https://plugins.dprint.dev/g-plane/malva-v0.15.3.wasm", - "https://plugins.dprint.dev/g-plane/markup_fmt-v0.27.0.wasm", - "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.6.0.wasm", - ], -} diff --git a/packages/eslint-shared-config/src/eslint.base.config.mjs b/packages/eslint-shared-config/src/eslint.base.config.mjs deleted file mode 100644 index f18c67d48..000000000 --- a/packages/eslint-shared-config/src/eslint.base.config.mjs +++ /dev/null @@ -1,114 +0,0 @@ -// @ts-nocheck -import js from "@eslint/js" -import tsPlugin from "@typescript-eslint/eslint-plugin" -import tsParser from "@typescript-eslint/parser" -import _import from "eslint-plugin-import" -import sortDestructureKeys from "eslint-plugin-sort-destructure-keys" -import unusedImports from "eslint-plugin-unused-imports" -import effectAppPlugin from "./plugin-effect-app.mjs" - -/** - * Minimal ESLint config. Type-aware TS rules and codegen also run via - * `oxlint --type-aware` (powered by tsgolint + the - * `@effect-app/eslint-codegen-model/oxlint` JS plugin) for non-vue packages. - * - * ESLint here is needed for vue packages (oxlint can't lint `.vue` files) and - * runs JS-based plugins (import, unused-imports, sort-destructure-keys), the - * `@typescript-eslint` plugin (so inline `eslint-disable @typescript-eslint/...` - * directives resolve and a small set of type-aware rules run on `.vue` files), - * plus the local `@effect-app/no-await-effect` rule (no tsgolint equivalent). - * Type-aware rules activate when ESLINT_TS=1 (or `forceTS`). - * - * @param {string} dirName - * @param {boolean} [forceTS=false] - * @returns {import("eslint").Linter.FlatConfig[]} - */ -export function baseConfig(dirName, forceTS = false) { - // eslint-disable-next-line no-undef - const enableTS = !!dirName && (forceTS || process.env["ESLINT_TS"]) - return [ - { - ignores: [ - "**/*.js", - "**/*.jsx", - "**/*.d.ts", - "**/node_modules/**", - "vitest.config.ts", - "vitest.config.test.ts", - "vite.config.ts", - "eslint.*.mjs" - ] - }, - js.configs.recommended, - { - name: "base", - files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts", "**/*.vue"], - languageOptions: { - parser: tsParser, - parserOptions: { - extraFileExtensions: [".vue"], - tsconfigRootDir: dirName, - ...(enableTS && { - projectService: true - }) - } - }, - linterOptions: { - reportUnusedDisableDirectives: "off" - }, - plugins: { - import: _import, - "sort-destructure-keys": sortDestructureKeys, - "unused-imports": unusedImports, - "@typescript-eslint": tsPlugin, - "@effect-app": effectAppPlugin - }, - rules: { - "no-unexpected-multiline": "off", - "no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "after-used", - "argsIgnorePattern": "^_", - "ignoreRestSiblings": true - } - ], - "sort-destructure-keys/sort-destructure-keys": "error", - "require-yield": "off", - "sort-imports": "off", - "import/first": "error", - "import/newline-after-import": "error", - "import/no-duplicates": ["error", { "prefer-inline": true }], - "import/no-unresolved": "off", - "import/order": "off", - "object-shorthand": "error", - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-use-before-define": ["warn", { functions: false, classes: true, variables: true }], - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/interface-name-prefix": "off", - "@typescript-eslint/no-empty-object-type": "off", - "@typescript-eslint/consistent-type-imports": ["error", { fixStyle: "inline-type-imports" }], - ...(enableTS && { - "@effect-app/no-await-effect": "error", - "@typescript-eslint/restrict-template-expressions": "warn", - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-call": "warn", - "@typescript-eslint/no-unsafe-return": "warn", - "@typescript-eslint/no-unsafe-argument": "warn", - "@typescript-eslint/no-unsafe-member-access": "warn", - "@typescript-eslint/no-misused-promises": "warn", - "@typescript-eslint/unbound-method": "error", - "@typescript-eslint/only-throw-error": "off", - "@typescript-eslint/no-base-to-string": "warn", - "@typescript-eslint/no-floating-promises": "error" - }) - } - } - ] -} diff --git a/packages/eslint-shared-config/src/eslint.vue.config.mjs b/packages/eslint-shared-config/src/eslint.vue.config.mjs deleted file mode 100644 index fddfca4e0..000000000 --- a/packages/eslint-shared-config/src/eslint.vue.config.mjs +++ /dev/null @@ -1,79 +0,0 @@ -import tsParser from "@typescript-eslint/parser" -import formatjs from "eslint-plugin-formatjs" -import pluginVue from "eslint-plugin-vue" -import globals from "globals" -import vueParser from "vue-eslint-parser" -import { baseConfig } from "./eslint.base.config.mjs" -import effectAppPlugin from "./plugin-effect-app.mjs" - -/** - * Vue ESLint config. Most type-aware TS rules are handled by `oxlint --type-aware` - * (powered by tsgolint). ESLint here is retained because oxlint cannot lint - * `.vue` files; it runs `eslint-plugin-vue` for template/SFC checks, `formatjs` - * for ICU messages, and the local `@effect-app/no-await-effect` type-aware rule - * (which has no tsgolint equivalent). `@typescript-eslint/parser` is used as a - * parser so vue-eslint-parser can read ` - ``` - -## 0.3.3 - -### Patch Changes - -- df2e948: Fixes meta.required wrong inheritance and adds at least storybook story, waiting for unit tests - -## 0.3.2 - -### Patch Changes - -- e7c3aff: deep merge default values which comes from persistency wrt tanstack def values - -## 0.3.1 - -### Patch Changes - -- c8e5220: update OmegaForm persistency configuration to use policies array and add query params option - -## 0.3.0 - -### Minor Changes - -- 6f33b83: Enhance OmegaForm functionality with persistency support and improved input handling. Added clear and reset methods to the form API, updated internal input state management, and refined form stories for better demonstration of features. - -## 0.2.8 - -### Patch Changes - -- 7fcd006: feat: add showErrorOn props in OmegaForm Wrapper components, to manage when to show the errors - -## 0.2.7 - -### Patch Changes - -- f7909fc: Explicitly pass down model-value - -## 0.2.6 - -### Patch Changes - -- 34abab7: Changes spread order in OmegaFormVuetify - -## 0.2.5 - -### Patch Changes - -- f6043f4: manually pass clear function - -## 0.2.4 - -### Patch Changes - -- b9efc9f: Moves from custom ui to standard vuetify ui for clearable selects -- 587a06a: Fixes model value reference inside vuetify components - -## 0.2.3 - -### Patch Changes - -- Updated dependencies [d3b05cb] -- Updated dependencies [4627c7b] -- Updated dependencies [1e1f32b] - - effect-app@2.39.1 - - @effect-app/vue@2.40.1 - -## 0.2.2 - -### Patch Changes - -- Updated dependencies [bdc9d5c] - - effect-app@2.39.0 - - @effect-app/vue@2.40.0 - -## 0.2.1 - -### Patch Changes - -- d01c3e9: Manages focus in OmegaInput - -## 0.2.0 - -### Minor Changes - -- 7ac3d9d: Adds checkbox default to OmegaForm - -## 0.1.3 - -### Patch Changes - -- 21b0696: Fix: OmegaInput Attributes spreading - -## 0.1.2 - -### Patch Changes - -- Updated dependencies [8b7fdbe] - - effect-app@2.38.5 - - @effect-app/vue@2.39.9 - -## 0.1.1 - -### Patch Changes - -- b7cfa49: Fixes default styling for reset in select and autocomplete - -## 0.1.0 - -### Minor Changes - -- 98d5344: Enhance OmegaForm components with new input types and improved structure - - Added support to CSS inside JS bundle both for modules than for custom elements - - OmegaFormInput now fully support children as input, so vuetify it's only a default for environments that globally import that library - - Updated OmegaInput.vue to support autocomplete and autocompletemultiple types. - - Refactored OmegaInternalInput.vue to streamline input handling and improve slot usage. - - Modified OmegaErrors.vue to utilize slots for better flexibility in error display. - - Added new types to OmegaFormStuff.ts for enhanced type safety. - - Adjusted Vite configuration to support custom elements in Vue components. - -## 0.0.9 - -### Patch Changes - -- Updated dependencies - - effect-app@2.38.4 - - @effect-app/vue@2.39.8 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies - - effect-app@2.38.3 - - @effect-app/vue@2.39.7 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [c0ed28a] -- Updated dependencies [0fcab38] - - effect-app@2.38.2 - - @effect-app/vue@2.39.6 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [cd15a8e] - - effect-app@2.38.1 - - @effect-app/vue@2.39.5 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [81479bf] - - effect-app@2.38.0 - - @effect-app/vue@2.39.4 - -## 0.0.4 - -### Patch Changes - -- 6e7dc86: add support for textarea and pass attrs to input. - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [7bedb13] - - effect-app@2.37.1 - - @effect-app/vue@2.39.3 - -## 0.0.2 - -### Patch Changes - -- ce0bfca: fix style - -## 0.0.1 - -### Patch Changes - -- c3a730c: initial vue-components diff --git a/packages/vue-components/README.md b/packages/vue-components/README.md deleted file mode 100644 index b7cea449f..000000000 --- a/packages/vue-components/README.md +++ /dev/null @@ -1,233 +0,0 @@ -[![Netlify Status](https://api.netlify.com/api/v1/badges/b1b84831-789e-4629-a9e3-55a36e136653/deploy-status)](https://app.netlify.com/sites/sharp-babbage-154f0a/deploys) - -# Vue Component Library Starter - -> Create your own [Vue 3](https://v3.vuejs.org/) component library with TypeScript, [Vite](https://vitejs.dev) and [VitePress](https://vitepress.vuejs.org/). - -Sooner or later, you will find that creating a component library is much better than having all components inside your app project. A component library force to you remove app specific logic from your components, making it easier to test and reuse them in other apps. - -Once the components are in a library, documentation becomes critical. This starter project includes a documentation app powered by VitePress. It not only documents the usage of the component, but also provides a testing bed during the development of components. See the generated documentation app [here](https://sharp-babbage-154f0a.netlify.com/). - -## Setup - -> When running `docs:dev` for the first time, you may encounter error like `vitepress data not properly injected in app` in your browser. Restart the server and reload the browser. Please refer to [issue #30](https://github.com/wuruoyun/vue-component-lib-starter/issues/30) for more details. - -```bash -# install dependencies -npm install - -# start the doc app with hot reload, great for testing components -npm run docs:dev - -# build the library, available under dist -npm run build - -# build the doc app, available under docs/.vitepress/dist -npm run docs:build - -# preview the doc app locally from docs/.vitepress/dist -npm run docs:serve -``` - -You may use [Netlify](https://www.netlify.com/) to auto build and deploy the doc app like this project does. - -## Develop and test locally - -The best way to develop and test your component is by creating demos in `docs/components/demo` folder, as shown by the example components. - -If you want to test the library in your Vue3 app locally: - -- In the root folder of this library, run `npm link`. This will create a symbolic link to the library. -- In the root folder of your client app, run `npm link my-lib`. This will add the symbolic link to the `node_modules` folder in your client app. -- You can now import `my-lib` in your client app. - -There is no need to add `my-lib` to your client app's dependency in this case. - -If you made changes to the library, you will need to rebuild the library. Your Vue3 app shall hot reload when the building of library is completed. - -## How it works - -### Components - -The library is a [Vue plugin](https://v3.vuejs.org/guide/plugins.html). The `install` function in [index.ts](src/index.ts) registers all components under [components](src/components) to Vue globably. - -The components are also exported by [index.ts](src/index.ts) so that the client app can import them individually and register them locally, instead of using the library as a plugin. This may be a better option if the client app only use a small set of components in your library. - -As there are already many UI component libraries for Vue 3, you may just want to build on top of one of them and create components for your specific needs. The Component B in this starter shows the example of using [PrimeVue](https://www.primefaces.org/primevue/) as the fundation library. However, this means the client app shall also use the same fundation component library as your library does. - -The doc app itself is a client app of the libary, therefore PrimeVue is imported in [docs/.vitepress/theme/index.js](docs/.vitepress/theme/index.js). The configuration in [docs/.vitepress/config.js](docs/.vitepress/config.js) below forces VitePress to resolve these modules with no duplication, avoiding error at runtime, as PrimeVue also has Vue in its dependency. - -```js -module.exports = { - vite: { - resolve: { - dedupe: ["vue", /primevue\/.+/] - } - } -} -``` - -> In [vite.config.ts](vite.config.ts), format 'umd' is not present in `build.lib.formats` option. This is because the PrimeVue components used by this library are externalized, and therefore requiring corresponding options in `rollupOptions.output.globals`. To avoid adding global varaibles for PrimeVue components, 'umd' is removed for simplicity. - -### Utilities and constants - -The library includes example utilities and constants. They are also exported in [index.ts](src/index.ts). The client app may use them as below: - -```js - -``` - -### Styling - -Individual components may have styles defined in its `.vue` file. They will be processed, combined and minified into `dist/style.css`, which is included in the `exports` list in [package.json](package.json). - -If you have library level styles shared by all components in the library, you may add them to [src/assets/main.scss](src/assets/main.scss). This file is imported in [index.ts](src/index.ts), therefore the processed styles are also included into `dist/style.css`. To avoid conflicting with other global styles, consider pre-fixing the class names or wrapping them into a namespace class. - -If you have your own special set of SVG icons, you may create a font file (`.woff` format) using tools like [Icomoon](https://icomoon.io/) or [Fontello](https://fontello.com/). This starter includes an example font file [src/assets/fonts/myfont.woff](src/assets/fonts/myfont.woff) and references it in [src/assets/main.scss](src/assets/main.scss), with utility icon CSS classes. An icon from the font file is used in Component A. Vite will include the font file into the build, see [https://vitejs.dev/guide/assets.html](https://vitejs.dev/guide/assets.html). - -The client app shall import `style.css`, usually in the entry file: - -```js -import "my-lib/dist/style.css" -``` - -### Third-party dependencies - -Third-party libraries used by you library may bloat up the size of your library, if you simply add them to the `dependencies` in [package.json](package.json). - -The following are some strategies to reduce the size of your library: - -#### Externalization - -If you expect the client app of your library may also need the same dependency, you may externalize the dependency. For example, to exclude PrimeVue from your library build artifact, in [vite.config.ts](vite.config.ts), you may have - -```js -module.exports = defineConfig({ - rollupOptions: { - external: ['vue', /primevue\/.+/] - } - } -}) -``` - -The dependency to be externalized may be declared as peer dependency in your library. - -#### Cherry picking - -If you don't expect the client app of your library also needing the same dependency, you may embed cherry-picked functions. For example, to embed the `fill` function of popular library [lodash](https://lodash.com), import the `fill` function like the following: - -```js -import fill from "lodash/fill" -``` - -Even with tree-shaking, the codes being brought into your library may still be large, as the function may have its own dependencies. - -Note that `import { fill } from 'lodash'` or `import _ from 'lodash'` will not work and will embed the whole `lodash` library. - -Finally, if your client app also use `lodash` and you don't want `lodash` to be in both the client app and your libraries, even after cherry-picking, you may consider cherry-picking in component library and re-export them as utils for client to consume, so that the client does not need to depend on `lodash`, therefore avoiding duplication. - -### Type generation - -In [tsconfig.json](tsconfig.json), the following options instructs `tsc` to emit declaration (`.d.ts` files) only, as `vite build` handles the `.js` file generation. The generated `.d.ts` files are sent to `dist/types` folder. - -```json -"compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "declarationDir": "./dist/types" -} -``` - -In [package.json](package.json), the line below locates the generated types for library client. - -```json -"types": "./dist/types/index.d.ts", -``` - -> In [vite.config.ts](vite.config.ts), `build.emptyOutDir` is set to `false` and `rimraf` is used instead to remove the `dist` folder before the build. This is to avoid the `dist/types` folder generated by `tsc` being deleted when running `vite build`. - -### Configuration - -#### TypeScript - -In [tsconfig.json](tsconfig.js), set the following as recommended by Vite (since esbuild is used). However, enableing this option leads to https://github.com/vitejs/vite/issues/5814. The workaround is to also enable `compilerOptions.skipLibCheck`. - -```json -"compilerOptions": { - "isolatedModules": true -} -``` - -In [tsconfig.json](tsconfig.js), set the following to address [Issue #32](https://github.com/wuruoyun/vue-component-lib-starter/issues/32). The solution is from https://github.com/johnsoncodehk/volar/discussions/592. - -```json -"compilerOptions": { - "types": [ - "vite/client" - ] -} -``` - -## v4 Breaking Changes (from v3) - -### 1. Vuetify 4 CSS Reset Removed - -Vuetify 4 removed the aggressive global CSS reset that v3 included (universal `margin: 0; padding: 0`, list/heading/input resets, etc.). If your app relied on these defaults, opt in to the supplemental reset: - -```ts -import "@effect-app/vue-components/reset.css" -``` - -### 2. Nested union `_tag` handling - -`S.TaggedStruct` produces a bare `Literal` AST node, while legacy `S.Struct({ _tag: S.Literal("X") })` produces `Union([Literal("X")])` after `AST.toType`. Both patterns now correctly produce `"select"` metadata thanks to the `unwrapSingleLiteralUnion` helper. A `console.warn` is emitted for the legacy pattern to encourage migration to `TaggedStruct`. - -### 3. `UndefinedOr` defaults include the key with explicit `undefined` - -- **v3**: `defaultsValueFromSchema` skipped keys where the recursive call returned `undefined`, so `UndefinedOr` fields were omitted from the result object entirely. -- **v4**: The key is present in the result with an explicit `undefined` value. - -### 4. `S.optionalKey(X).pipe(S.withDecodingDefault(...))` support - -- **v3**: Not supported. The v3 equivalent `S.optionalWith` encoded defaults inside a `PropertySignatureTransformation`, opaque to AST inspection. -- **v4**: `defaultsValueFromSchema` detects `PropertySignatureTransformation` and extracts defaults, enabling `withDecodingDefault` as a new pattern for declaring field defaults directly on the schema. - -### On Submit event - -The :on-submit event could be tricky in `` component. -This is a prop that is basically a map of Tanstack Form `onSubmit` option and accept a function that return a Promise. If you want to use it as an event, you have to manage the state of loading yourself with `@submit` with a function returning `void` - -E.g. - -to use it as a Promise based Tanstack way: - -```html - -``` - -to use it as an event: - -```html - -``` - -this will give you a type error instead, because `:is-loading` is only accepted in event mode - -```html - -``` diff --git a/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts b/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts deleted file mode 100644 index 2e88b1f55..000000000 --- a/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { mount } from "@vue/test-utils" -import { CauseException } from "effect-app/client" -import * as Cause from "effect/Cause" -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" -import { defineComponent, h, nextTick, onErrorCaptured, ref } from "vue" -import { createMemoryHistory, createRouter, type Router } from "vue-router" -import FixedNuxtErrorBoundary from "../src/components/FixedNuxtErrorBoundary.vue" - -/** - * Behavior contract of the shared error boundary (extracted from the two - * app-local copies — this pins the "no behavior change" claim of the dedup): - * - a supported error (e.g. thrown in setup) renders the error slot and - * emits "error"; it is NOT reported via captureException - * - an unsupported error (e.g. native event handler) is reported via - * captureException (+ toast when debug) and does NOT take over the page - * - an interrupts-only Effect CauseException is ignored entirely - * - a route change clears the error state - * - scheduleAfterReady defers handling (backend hydration path) - * - enabled=false registers no handler, so errors propagate to the parent - */ - -const noopWarn = () => {} - -// Throws in setup only while `active` is true — a supported error ("setup -// function"), and togglable so the route-change test can stop re-throwing. -const makeThrowingChild = (active: { value: boolean }, err: unknown) => - defineComponent({ - name: "ThrowingChild", - setup() { - if (active.value) throw err - return () => h("div", { id: "child-ok" }, "ok") - } - }) - -// Throws from a native click handler — an unsupported error info. -const ClickThrowChild = defineComponent({ - name: "ClickThrowChild", - setup() { - return () => - h("button", { - id: "boom", - onClick: () => { - throw new Error("click boom") - } - }, "boom") - } -}) - -const makeRouter = () => - createRouter({ - history: createMemoryHistory(), - routes: [ - { path: "/", component: { render: () => null } }, - { path: "/other", component: { render: () => null } } - ] - }) - -describe("FixedNuxtErrorBoundary", () => { - let captureException: ReturnType - let toastError: ReturnType - let router: Router - let warnSpy: ReturnType - - beforeEach(() => { - captureException = vi.fn() - toastError = vi.fn() - router = makeRouter() - warnSpy = vi.spyOn(console, "warn").mockImplementation(noopWarn) - }) - - afterEach(() => { - warnSpy.mockRestore() - }) - - // Mount through a host component that renders the boundary with its slots, - // mirroring real usage (Suspender wraps it). Mounting the boundary directly - // as the test root breaks error propagation into its own onErrorCaptured. - const mountBoundary = ( - child: ReturnType, - props: Record = {} - ) => { - const emitted: Error[] = [] - const Host = defineComponent({ - name: "Host", - setup() { - return () => - h(FixedNuxtErrorBoundary, { - captureException, - toastError, - debug: true, - onError: (e: Error) => emitted.push(e), - ...props - }, { - default: () => h(child), - error: (params: { error: Error; clearError: () => void }) => - h("div", { id: "error-slot" }, params.error.message) - }) - } - }) - const wrapper = mount(Host, { global: { plugins: [router] } }) - return { wrapper, emitted } - } - - it("renders the error slot and emits on a supported (setup) error, without reporting it", async () => { - const active = ref(true) - const { emitted, wrapper } = mountBoundary(makeThrowingChild(active, new Error("setup boom"))) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(true) - expect(wrapper.find("#error-slot").text()).toBe("setup boom") - expect(emitted).toHaveLength(1) - expect(captureException).not.toHaveBeenCalled() - expect(warnSpy).toHaveBeenCalled() - }) - - it("reports an unsupported (native handler) error via captureException + toast, without taking over the page", async () => { - const { emitted, wrapper } = mountBoundary(ClickThrowChild) - await wrapper.find("#boom").trigger("click") - await nextTick() - - expect(captureException).toHaveBeenCalledTimes(1) - expect(captureException.mock.calls[0][0]).toBeInstanceOf(Error) - expect(toastError).toHaveBeenCalledWith("An unexpected error has occurred: Error: click boom") - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(emitted).toHaveLength(0) - }) - - it("does not toast an unsupported error when debug is off", async () => { - const { wrapper } = mountBoundary(ClickThrowChild, { debug: false }) - await wrapper.find("#boom").trigger("click") - - expect(captureException).toHaveBeenCalledTimes(1) - expect(toastError).not.toHaveBeenCalled() - }) - - it("ignores an interrupts-only Effect CauseException", async () => { - const interrupted = new CauseException(Cause.interrupt(), "Interrupted") - const { emitted, wrapper } = mountBoundary(makeThrowingChild(ref(true), interrupted)) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(emitted).toHaveLength(0) - expect(captureException).not.toHaveBeenCalled() - }) - - it("clears the error on route change", async () => { - const active = ref(true) - const { wrapper } = mountBoundary(makeThrowingChild(active, new Error("boom"))) - await nextTick() - expect(wrapper.find("#error-slot").exists()).toBe(true) - - active.value = false - await router.push("/other") - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(wrapper.find("#child-ok").exists()).toBe(true) - }) - - it("defers handling through scheduleAfterReady when provided", async () => { - const queue: (() => void)[] = [] - const { wrapper } = mountBoundary(makeThrowingChild(ref(true), new Error("deferred boom")), { - scheduleAfterReady: (fn: () => void) => queue.push(fn) - }) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(queue).toHaveLength(1) - - queue.forEach((fn) => fn()) - await nextTick() - expect(wrapper.find("#error-slot").text()).toBe("deferred boom") - }) - - it("calls onHandled after emitting, before rendering the error slot", async () => { - const onHandled = vi.fn() - const { wrapper } = mountBoundary(makeThrowingChild(ref(true), new Error("hooked boom")), { onHandled }) - await nextTick() - - expect(onHandled).toHaveBeenCalledTimes(1) - expect(onHandled.mock.calls[0][0]).toBeInstanceOf(Error) - expect(onHandled.mock.calls[0][2]).toBe("setup function") - expect(wrapper.find("#error-slot").exists()).toBe(true) - }) - - it("registers no handler when enabled is false, letting the error propagate", async () => { - const parentCaught = vi.fn() - const Host = defineComponent({ - name: "Host", - setup() { - onErrorCaptured((err) => { - parentCaught(err) - return false - }) - return () => - h(FixedNuxtErrorBoundary, { - captureException, - toastError, - debug: true, - enabled: false - }, { - default: () => h(makeThrowingChild(ref(true), new Error("escaped boom"))), - error: () => h("div", { id: "error-slot" }) - }) - } - }) - const wrapper = mount(Host, { global: { plugins: [router] } }) - await nextTick() - - expect(parentCaught).toHaveBeenCalledTimes(1) - expect(wrapper.find("#error-slot").exists()).toBe(false) - }) -}) diff --git a/packages/vue-components/__tests__/IntegerValidation.test.ts b/packages/vue-components/__tests__/IntegerValidation.test.ts deleted file mode 100644 index 8ed3d9935..000000000 --- a/packages/vue-components/__tests__/IntegerValidation.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, type NumberFieldMeta, toLocalizedStandardSchemaV1 } from "../src/components/OmegaForm" - -const germanTranslations: Record = { - "validation.integer.expected": "Es wird eine ganze Zahl erwartet, tatsächlich: {actualValue}", - "validation.number.expected": "Es wird eine Zahl erwartet, tatsächlich: {actualValue}", - "validation.empty": "Das Feld darf nicht leer sein" -} - -const germanTrans = (id: string, values?: Record) => { - let text = germanTranslations[id] || id - if (values) { - for (const [k, v] of Object.entries(values)) text = text.replace(`{${k}}`, String(v)) - } - return text -} - -describe("Integer validation with German translations (form-level localized schema)", () => { - it("should generate number metadata with int refinement for S.Int fields", () => { - const TestSchema = S.Struct({ value: S.Int }) - const { meta } = generateMetaFromSchema(TestSchema) - expect(meta.value?.type).toBe("number") - expect((meta.value as NumberFieldMeta).refinement).toBe("int") - }) - - it("shows German error for decimal value at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: 59.5 }) - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "value") - expect(issue?.message).toContain("ganze Zahl") - }) - - it("shows German empty message for undefined required int at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: undefined }) - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "value") - expect(issue?.message).toBe("Das Feld darf nicht leer sein") - }) - - it("accepts valid integer at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: 59 }) - expect(result.issues).toBeUndefined() - expect("value" in result && (result as any).value).toEqual({ value: 59 }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm.test.ts b/packages/vue-components/__tests__/OmegaForm.test.ts deleted file mode 100644 index cc5925957..000000000 --- a/packages/vue-components/__tests__/OmegaForm.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { NonEmptyString255 } from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, type MetaRecord } from "../src/components/OmegaForm" -import OmegaForm from "./OmegaFormTest.vue" -import OmegaIntlProvider from "./OmegaIntlProvider.vue" - -class kkk extends S.Class("kkk")({ - lll: S.String.pipe(S.check(S.isMinLength(10)), S.check(S.isMaxLength(21))), - mmm: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) -}) {} - -class bbb extends S.Class("bbb")({ - ccc: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(2))), - ddd: S.NullOr(S.String.pipe(S.check(S.isMaxLength(3)))), - jjj: S.UndefinedOr(S.String.pipe(S.check(S.isMaxLength(4)))), - nnn: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - kkk, - ooo: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(23))) -}) {} - -describe("OmegaForm", () => { - describe("addRecursiveAnnotations", () => { - it("should properly extract metadata from complex schema", () => { - const testSchema = S.Struct({ - aaa: NonEmptyString255, - bbb: S.NullOr(bbb), - eee: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - fff: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]), - zzz: S.NullOr(S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 })))).pipe( - S.annotate({ - message: () => "foobar" - }) - ) - }) - - const expectedMeta = { - aaa: { - maxLength: 255, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.ccc": { - maxLength: 2, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.ddd": { - maxLength: 3, - nullableOrUndefined: "null", - required: false, - type: "string" - }, - "bbb.jjj": { - maxLength: 4, - nullableOrUndefined: "undefined", - required: false, - type: "string" - }, - "bbb.kkk.lll": { - maxLength: 21, - minLength: 10, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.kkk.mmm": { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - "bbb.nnn": { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - "bbb.ooo": { - maxLength: 23, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - eee: { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - fff: { - members: ["left", "right", "both"], - - nullableOrUndefined: false, - required: true, - type: "select" - }, - zzz: { - maximum: 20, - minimum: 10, - nullableOrUndefined: "null", - required: false, - type: "number" - } - } - - const result = generateMetaFromSchema(testSchema) - - expect(result).toHaveProperty("schema") - expect(result).toHaveProperty("meta") - - expect(result.schema).toBe(testSchema) - - expect(result.meta).toEqual(expectedMeta) - }) - }) - - describe("generateMetaFromSchema", () => { - it("should generate meta from schema", () => { - const testSchema = S.Struct({ - aaa: S.String, - bbb: S.NullOr( - S.Struct({ - ccc: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(2))), - ddd: S.NullOr(S.String.pipe(S.check(S.isMaxLength(3)))), - jjj: S.UndefinedOr(S.String.pipe(S.check(S.isMaxLength(4)))), - nnn: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - kkk: S.Struct({ - lll: S.String.pipe(S.check(S.isMinLength(10)), S.check(S.isMaxLength(21))), - mmm: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) - }), - ooo: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(23))) - }) - ), - eee: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - fff: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]), - zzz: S.NullOr(S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 })))) - }) - - const result = generateMetaFromSchema(testSchema) - - // Type check: ensure the meta record has the correct keys - type TestType = typeof testSchema extends S.Codec ? T : never - const meta: MetaRecord = result.meta - - // Value check - expect(meta["aaa"]).toBeDefined() - expect(meta["bbb.ccc"]).toBeDefined() - expect(meta["bbb.kkk.lll"]).toBeDefined() - expect(meta["eee"]).toBeDefined() - expect(meta["fff"]).toBeDefined() - expect(result.schema).toBe(testSchema) - }) - }) -}) - -// Create a wrapper component that includes the OmegaIntlProvider -const OmegaFormWithProvider = { - components: { OmegaForm, OmegaIntlProvider }, - template: ` - - - - ` -} - -describe("OmegaForm UI", () => { - it("renders", () => { - const wrapper = mount(OmegaFormWithProvider) - expect(wrapper.exists()).toBe(true) - }) - - it("should display the sum of first and second inputs", async () => { - const wrapper = mount(OmegaFormWithProvider) - - // Find the input elements - const firstInput = wrapper.find("input[id=\"first\"]") - const secondInput = wrapper.find("input[id=\"second\"]") - - console.log(firstInput) - - // Set values - await firstInput.setValue(10) - await secondInput.setValue(20) - - // Trigger change events - await firstInput.trigger("change") - await secondInput.trigger("change") - - // Get the element showing the sum - const sumElement = wrapper.find("[data-testid=\"valuez\"]") - - // Assert the sum is correct (10 + 20 = 30) - expect(sumElement.text()).toBe("30") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts b/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts deleted file mode 100644 index c893d391e..000000000 --- a/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { toLocalizedStandardSchemaV1 } from "../../src/components/OmegaForm" - -const identityTrans = (id: string, values?: Record) => { - if (!values) return id - return Object.entries(values).reduce( - (acc, [k, v]) => acc.replaceAll(`{${k}}`, String(v)), - id - ) -} - -const maxLineLengthCheck = (max: number) => - S.makeFilter((value: string) => { - const tooLong = value.split("\n").find((line) => line.length > max) - return tooLong !== undefined ? `Line "${tooLong}" exceeds ${max} chars` : undefined - }) - -describe("OmegaForm form-level schema custom checks", () => { - it("includes custom filter checks in the form-level localized schema", async () => { - const schema = S.Struct({ - height: S.NonEmptyString100.pipe( - S.check(S.isMinLength(10)), - S.check(maxLineLengthCheck(20)) - ) - }) - - const standardSchema = toLocalizedStandardSchemaV1(schema as any, identityTrans) - const invalid = "1234567890\n123456789012345678901" - const result = await standardSchema["~standard"].validate({ height: invalid }) - - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "height") - expect(issue).toBeDefined() - expect(issue?.message).toContain("exceeds 20 chars") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts deleted file mode 100644 index 9d794a84d..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Date field validation", () => { - it("accepts a raw YYYY-MM-DD string and decodes it to Date on submit", async () => { - let submittedValue: { date: Date } | null = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - date: S.DateFromString - }), - { - onSubmit: async ({ value }) => { - submittedValue = value - } - } - ) - - return { form } - } - }) - - await wrapper.find("[data-testid='date-input']").setValue("2024-06-01") - await wrapper.find("[data-testid='submit']").trigger("click") - - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - expect(submittedValue?.date).toBeInstanceOf(Date) - expect(submittedValue?.date.toISOString()).toContain("2024-06-01") - expect(wrapper.find("[data-testid='errors']").text()).toBe("[]") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts b/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts deleted file mode 100644 index 9b2d7045c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, expect, it } from "vitest" -import { handleDecimalSeparatorBeforeinput } from "../../src/components/OmegaForm/decimalSeparatorInput" - -// Simulates typing into a VNumberInput: the handler is attached in capture -// phase on the component root, the native input is the event target. -const setup = ( - value: string, - separator: string, - cursor?: { start: number; end: number } -) => { - const root = document.createElement("div") - const input = document.createElement("input") - root.appendChild(input) - document.body.appendChild(root) - input.value = value - const start = cursor?.start ?? value.length - const end = cursor?.end ?? value.length - input.setSelectionRange(start, end) - - let vuetifySawBeforeinput = false - let inputEventFired = false - input.addEventListener("beforeinput", () => { - vuetifySawBeforeinput = true - }) - input.addEventListener("input", () => { - inputEventFired = true - }) - root.addEventListener( - "beforeinput", - (e) => handleDecimalSeparatorBeforeinput(e, separator), - { capture: true } - ) - - const type = (data: string) => { - const e = new InputEvent("beforeinput", { - data, - inputType: "insertText", - bubbles: true, - cancelable: true - }) - input.dispatchEvent(e) - return e - } - - return { - input, - type, - sawVuetifyHandler: () => vuetifySawBeforeinput, - sawInputEvent: () => inputEventFired - } -} - -describe("handleDecimalSeparatorBeforeinput", () => { - it("translates '.' into ',' when the active separator is ','", () => { - const t = setup("1", ",") - const e = t.type(".") - expect(e.defaultPrevented).toBe(true) - expect(t.sawVuetifyHandler()).toBe(false) - expect(t.input.value).toBe("1,") - expect(t.sawInputEvent()).toBe(true) - }) - - it("translates ',' into '.' when the active separator is '.'", () => { - const t = setup("1", ".") - t.type(",") - expect(t.input.value).toBe("1.") - }) - - it("translates both '.' and ',' when the active separator is a custom one", () => { - const dot = setup("1", "٫") - dot.type(".") - expect(dot.input.value).toBe("1٫") - - const comma = setup("2", "٫") - comma.type(",") - expect(comma.input.value).toBe("2٫") - }) - - it("leaves the event alone when the typed char is the active separator", () => { - const t = setup("1", ",") - const e = t.type(",") - expect(e.defaultPrevented).toBe(false) - expect(t.sawVuetifyHandler()).toBe(true) - expect(t.input.value).toBe("1") - }) - - it("leaves plain digits alone", () => { - const t = setup("1", ",") - const e = t.type("5") - expect(e.defaultPrevented).toBe(false) - expect(t.sawVuetifyHandler()).toBe(true) - }) - - it("swallows the wrong separator when the value already has one", () => { - const t = setup("1,5", ",") - const e = t.type(".") - expect(e.defaultPrevented).toBe(true) - expect(t.input.value).toBe("1,5") - expect(t.sawInputEvent()).toBe(false) - }) - - it("inserts at the cursor position and replaces the selection", () => { - const t = setup("15", ",", { start: 1, end: 1 }) - t.type(".") - expect(t.input.value).toBe("1,5") - expect(t.input.selectionStart).toBe(2) - expect(t.input.selectionEnd).toBe(2) - }) - - it("normalizes the wrong separator inside pasted data", () => { - const t = setup("", ",") - t.type("1.5") - expect(t.input.value).toBe("1,5") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts b/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts deleted file mode 100644 index 3abd2812c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import { describe, expect, it } from "vitest" -import { createUseFormWithCustomInput, generateMetaFromSchema, type NumberFieldMeta, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const CustomNumberInput = { - template: ` -

- `, - props: ["inputProps", "field"], - inheritAttrs: false -} - -describe("OmegaForm decodeTo fallback transform", () => { - const inputSchema = S.Struct({ amount: S.PositiveInt }) - - const transformedSchema = S.Struct({ amount: S.NonNegativeInt }).pipe( - S.decodeTo(inputSchema, { - decode: SchemaGetter.transform((input: { amount: number }) => input.amount === 0 ? { amount: 666 } : input), - encode: SchemaGetter.passthrough({ strict: false }) - }) - ) - - const createWrapper = (defaultValues: { amount: number }) => { - let submittedValue: any = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(transformedSchema, { - defaultValues, - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }) - - return wrapper - } - - it("should accept 0 at the input boundary and pass 666 to onSubmit via decode", async () => { - const wrapper = createWrapper({ amount: 0 }) - await wrapper.vm.$nextTick() - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 666 }) - }) - - it("should pass through a positive amount unchanged", async () => { - const wrapper = createWrapper({ amount: 5 }) - await wrapper.vm.$nextTick() - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 5 }) - }) - - it("meta extraction should reflect the outer (source) schema, not the inner decoded one", () => { - const { meta } = generateMetaFromSchema(transformedSchema) - const amount = meta.amount as NumberFieldMeta | undefined - - expect(amount).toBeDefined() - expect(amount!.type).toBe("number") - expect(amount!.refinement).toBe("int") - // NonNegativeInt ⇒ minimum: 0 (not exclusiveMinimum: 0 from PositiveInt) - expect(amount!.minimum).toBe(0) - expect(amount!.exclusiveMinimum).toBeUndefined() - }) - - it("should allow 0 typed into form.Input and submit with 666 via decode", async () => { - const useForm = createUseFormWithCustomInput(CustomNumberInput) - let submittedValue: any = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm(transformedSchema, { - defaultValues: { amount: 1 }, - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { stubs: { CustomNumberInput } } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("[data-testid=\"custom-input-field\"]") - await input.setValue("0") - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"custom-input-error\"]").exists()).toBe(false) - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 666 }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts b/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts deleted file mode 100644 index cce2d6ff4..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// __tests__/OmegaForm/DefaultValuesPriority.test.ts -import { describe, expect, it } from "vitest" -import { deepMerge } from "../../src/components/OmegaForm" - -describe("deepMerge", () => { - it("treats arrays as values, not as merged structures", () => { - expect(deepMerge({ xs: [1, 2, 3] }, { xs: [9] })).toEqual({ xs: [9] }) - }) - - it("recursively merges objects", () => { - expect(deepMerge({ a: { b: 1, c: 2 } }, { a: { c: 9, d: 3 } })) - .toEqual({ a: { b: 1, c: 9, d: 3 } }) - }) - - it("source wins for primitives", () => { - expect(deepMerge({ x: 1 }, { x: 2 })).toEqual({ x: 2 }) - }) -}) - -// NOTE: defaultValuesPriority resolution lives inside useOmegaForm's -// `defaultValues` computed and isn't exported. A behavioral test would mount -// a form. The deepMerge unit tests above pin the merge mechanic; the priority -// resolution itself is exercised indirectly by the Meta and Defaults stories. diff --git a/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts b/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts deleted file mode 100644 index 320d85dcd..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts +++ /dev/null @@ -1,551 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("OmegaForm Defaults", () => { - // Setup the same schemas as in Defaults.vue story - const struct = { - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: S.NonEmptyString, - c: S.NonEmptyArray(S.String).pipe( - S.withConstructorDefault(Effect.sync(() => [S.NonEmptyString("C"), S.NonEmptyString("Non Empty Array")])) - ), - d: S - .NonEmptyArray(S.Struct({ - e: S.NonEmptyString - })) - .pipe( - S.withConstructorDefault(Effect.sync(() => [{ e: S.NonEmptyString("default") }])) - ), - f: S.Union([ - S.Struct({ - _tag: S.Literal("taggo1").pipe(S.withConstructorDefault(Effect.succeed("taggo1"))), - g: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - i: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))) - }), - S.Struct({ - _tag: S.Literal("taggo2").pipe(S.withConstructorDefault(Effect.succeed("taggo2"))), - h: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - i: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))) - }) - ]), - j: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))), - k: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(true))), - l: S.NullOr( - S.Union([ - S.Struct({ - a: S.NonEmptyString255, - common: S.NonEmptyString255, - _tag: S.Literal("A") - }), - S.Struct({ - b: S.NonEmptyString255, - common: S.NonEmptyString255, - _tag: S.Literal("B") - }) - ]) - ), - m: S.Struct({ - n: S.NullOr(S.Struct({ q: S.String })), - o: S.UndefinedOr(S.Struct({ q: S.String })) - }), - p: S.NullOr(S.Struct({ z: S.String })), - q: S.UndefinedOr(S.Struct({ z: S.String })), - r: S - .NullOr(S.Struct({ - p: S.NullOr(S.Struct({ z: S.String })), - r: S.UndefinedOr(S.Struct({ z: S.String })) - })) - .withConstructorDefault, - s: S.NullOr(S.Struct({ z: S.String })).withConstructorDefault, - t: S.FiniteFromString.pipe(S.withConstructorDefault(Effect.succeed(1000))), - u: S.NullOr(S.NonEmptyString), - v: S.UndefinedOr(S.NonEmptyString) - } - - class ClassSchema extends S.Class("ClassSchema")(struct) {} - const schema = S.Struct(struct) - - const Union = S.Union([ - S.Struct({ - _tag: S.Literal("tag1").pipe(S.withConstructorDefault(Effect.succeed("tag1"))), - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: schema - }), - S.Struct({ - _tag: S.Literal("tag2"), - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - c: schema - }) - ]) - - it("should have correct default values for form zero (ClassSchema)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form one (schema)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form two (Union)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(Union) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "_tag": "tag1", - "a": "default", - "b": "default", - "c": { - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - } - }) - }) - - it("should not initialize _tag for a root union without explicit defaults", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(S.Union([ - S.Struct({ - _tag: S.Literal("one"), - a: S.Struct({ z: S.NonEmptyString100 }) - }), - S.Struct({ - _tag: S.Literal("two"), - a: S.Struct({ y: S.NonNegativeInt }) - }) - ])) - - return { form } - } - }) - - await wrapper.vm.$nextTick() - - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(JSON.parse(valuesText)).not.toHaveProperty("_tag") - }) - - it("should have correct default values for form three (with defaultValues merged)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form four (defaultFromSchema: only)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["schema"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form five (defaultFromSchema: nope)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["tanstack"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah" - }) - }) - - it("should have correct default values for form six (defaultFromSchema: merge)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["tanstack", "schema"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form seven (Union with different structure)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(S.Union([ - S.Struct({ - _tag: S.Literal("tag1").pipe(S.withConstructorDefault(Effect.succeed("tag1"))), - a: S.NonEmptyString, - s: S.NullOr(S.Finite).withConstructorDefault - }), - S.Struct({ - _tag: S.Literal("tag2"), - b: S.NonEmptyString, - t: S.Finite - }) - ])) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "_tag": "tag1", - "a": "", - "s": null, - "b": "" - }) - }) - - it("should have correct default values for form eight (ClassSchema with filter)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema - .pipe( - S.check(S.makeFilter((form) => { - if (form.a !== form.b) { - return { - path: ["a"], - message: "Email and confirmation must match!" - } - } - })) - )) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form nine (ClassSchema with filter inline)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema.pipe(S.check(S.makeFilter((form) => { - if (form.a !== form.b) { - return { - path: ["a"], - message: "Email and confirmation must match!" - } - } - })))) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts b/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts deleted file mode 100644 index dec4f4981..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -// __tests__/OmegaForm/Defaults.values.test.ts -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defaultsValueFromSchema } from "../../src/components/OmegaForm" -import { fillNestedDefaults } from "../../src/components/OmegaForm/meta/defaults" - -describe("defaultsValueFromSchema", () => { - it("extracts withConstructorDefault values", () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("Bob"))) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ name: "Bob" }) - }) - - it("extracts withDecodingDefault on optionalKey", () => { - const schema = S.Struct({ - flag: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("on"))) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ flag: "on" }) - }) - - it("returns null for NullOr fields without explicit default", () => { - const schema = S.Struct({ - x: S.NullOr(S.String) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: null }) - }) - - it("returns undefined for UndefinedOr fields without explicit default", () => { - const schema = S.Struct({ - x: S.UndefinedOr(S.Finite) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: undefined }) - }) - - it("returns empty string for plain S.String at the leaf", () => { - const schema = S.Struct({ x: S.String }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: "" }) - }) - - it("returns false for plain S.Boolean at the leaf", () => { - const schema = S.Struct({ x: S.Boolean }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: false }) - }) - - it("preserves values passed via the record argument", () => { - const schema = S.Struct({ x: S.String, y: S.String }) - expect(defaultsValueFromSchema(schema, { x: "preset" })).toEqual({ x: "preset", y: "" }) - }) - - it("merges fields across union members, picking explicit defaults", () => { - const schema = S.Union([ - S.TaggedStruct("A", { v: S.String.pipe(S.withConstructorDefault(Effect.succeed("a-default"))) }), - S.TaggedStruct("B", { v: S.String, extra: S.String }) - ]) - expect(defaultsValueFromSchema(schema)).toEqual({ _tag: "A", v: "a-default", extra: "" }) - }) -}) - -describe("fillNestedDefaults", () => { - it("fills missing nullable children for a materialized nullable struct", () => { - const schema = S.Struct({ - override: S.NullOr(S.Struct({ - min: S.NullOr(S.NonNegativeNumber), - max: S.NullOr(S.NonNegativeNumber) - })) - }) - - expect(fillNestedDefaults(schema.ast, { override: { min: 100 } })).toEqual({ - override: { - min: 100, - max: null - } - }) - }) - - it("does not add fields from another tagged union branch", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.NullOr(S.String), common: S.String }), - S.TaggedStruct("B", { b: S.Number, nullableB: S.NullOr(S.Number) }) - ]) - - expect(fillNestedDefaults(schema.ast, { _tag: "B", b: 1 })).toEqual({ - _tag: "B", - b: 1 - }) - }) - - it("fills missing nullable children for materialized struct elements of an array", () => { - const schema = S.Struct({ - items: S.Array(S.NullOr(S.Struct({ - a: S.NullOr(S.String), - b: S.NullOr(S.String) - }))) - }) - - expect(fillNestedDefaults(schema.ast, { items: [{ a: "x" }, null] })).toEqual({ - items: [{ a: "x", b: null }, null] - }) - }) - - it("returns the same reference when there is nothing to fill", () => { - const schema = S.Struct({ - items: S.Array(S.NullOr(S.Struct({ - a: S.NullOr(S.String), - b: S.NullOr(S.String) - }))) - }) - - const value = { items: [{ a: "x", b: null }, null] } - expect(fillNestedDefaults(schema.ast, value)).toBe(value) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts b/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts deleted file mode 100644 index 234848dc5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { mount } from "@vue/test-utils" -import { describe, expect, it, vi } from "vitest" -import { defineComponent, h, ref } from "vue" -import { provideBus, usePreventClose } from "../../src/components/OmegaForm/blockDialog" -import { useIntlKey } from "../../src/utils" - -// Regression: when the dialog-closing handler ran with a dirty form, the -// previous fix destructured `formatMessage` from `useIntl()`. The storybook -// mocks shipped a shape without a top-level `formatMessage`, so the -// destructured value was `undefined` and triggered "formatMessage is not a -// function". This test pins the production wiring: as long as `useIntl()` -// exposes `formatMessage` directly (real shape from `@effect-app/vue`), -// the confirm message is the formatted string. -describe("usePreventClose i18n", () => { - it("calls window.confirm with the formatted message when dirty and dialog is closing", () => { - const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(true) - const formatMessage = vi.fn( - (descriptor: { id: string; defaultMessage?: string }) => `de:${descriptor.id}` - ) - - let bus: ReturnType | undefined - const Outer = defineComponent({ - setup() { - bus = provideBus() - return () => h(Inner) - } - }) - const Inner = defineComponent({ - setup() { - usePreventClose(() => ref(true)) - return () => h("div") - } - }) - - mount(Outer, { - global: { - provide: { - [useIntlKey as unknown as symbol]: () => ({ - trans: (id: string) => id, - formatMessage - }) - } - } - }) - - bus!.emit("dialog-closing", {}) - - expect(formatMessage).toHaveBeenCalledTimes(1) - expect(formatMessage.mock.calls[0]?.[0]).toMatchObject({ id: "form.unsaved_changes_confirm" }) - expect(confirmSpy).toHaveBeenCalledWith("de:form.unsaved_changes_confirm") - - confirmSpy.mockRestore() - }) - - it("skips the confirm entirely when the form is not dirty", () => { - const confirmSpy = vi.spyOn(window, "confirm") - - let bus: ReturnType | undefined - const Outer = defineComponent({ - setup() { - bus = provideBus() - return () => h(Inner) - } - }) - const Inner = defineComponent({ - setup() { - usePreventClose(() => ref(false)) - return () => h("div") - } - }) - - mount(Outer) - - const evt: { prevent?: boolean | Promise } = {} - bus!.emit("dialog-closing", evt) - - expect(confirmSpy).not.toHaveBeenCalled() - expect(evt.prevent).toBeUndefined() - - confirmSpy.mockRestore() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts b/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts deleted file mode 100644 index ff1a800bf..000000000 --- a/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -describe("S.Email format detection", () => { - it("flags S.Email fields with format: 'email' on their meta", () => { - const schema = S.Struct({ x: S.Email }) - const { meta } = generateMetaFromSchema(schema) - expect((meta.x as any)?.format).toBe("email") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts deleted file mode 100644 index be930ee75..000000000 --- a/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { flushPromises, mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Empty string validation for nested NonEmptyString fields", () => { - it("demonstrates current empty string behavior", async () => { - const schema = S.Struct({ - asder2: S.Struct({ - value: S.NonEmptyString100 - }) - }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - asder2: { - value: "" - } - }, - onSubmit: async ({ value }) => { - console.log("Form submitted with:", value) - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - // Click submit with empty string - await wrapper.find("[data-testid='submit']").trigger("click") - await wrapper.vm.$nextTick() - await flushPromises() - - // Check for validation error - expect(wrapper.text()).toContain("validation.empty") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts b/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts deleted file mode 100644 index 33a03f7ea..000000000 --- a/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, h, nextTick } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaErrorsInternal from "../../src/components/OmegaForm/OmegaErrorsInternal.vue" - -describe("OmegaForm general errors", () => { - it("renders a string returned by an onSubmit validator and clears it after correction", async () => { - const message = "Passwörter stimmen nicht überein" - const Inner = defineComponent({ - setup() { - const form = useOmegaForm(S.Struct({ password: S.String, confirmation: S.String }), { - defaultValues: { password: "secret", confirmation: "different" }, - validators: { - onSubmit: ({ value }) => value.password === value.confirmation ? undefined : message - } - }) - return { form } - }, - template: `` - }) - const wrapper = mount(Inner) - - await wrapper.vm.form.handleSubmit() - await nextTick() - expect(wrapper.get("[role=\"alert\"]").text()).toContain(message) - - wrapper.vm.form.setFieldValue("confirmation", "secret") - await wrapper.vm.form.handleSubmit() - await nextTick() - expect(wrapper.find("[role=\"alert\"]").exists()).toBe(false) - wrapper.unmount() - }) - - it("preserves schema messages alongside strings and ignores unsupported values", () => { - const wrapper = mount(OmegaErrorsInternal, { - props: { - errors: [], - generalErrors: [ - "Form error", - { rows: [{ message: "Schema error" }, null, 42, { message: 123 }, { message: "" }] }, - null, - undefined, - false, - 42, - "", - { rows: "unsupported", other: null } - ] - }, - slots: { - default: ({ showedGeneralErrors }) => h("div", JSON.stringify(showedGeneralErrors)) - } - }) - - expect(wrapper.text()).toBe(JSON.stringify(["Form error", "Schema error"])) - wrapper.unmount() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts b/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts deleted file mode 100644 index 5023651bd..000000000 --- a/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick, ref } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" - -describe("OmegaForm indexed inputs", () => { - it.each([0, 13])("renders rows[%i].text outside form.Array with one labelled error", async (index) => { - const wrapper = mount(defineComponent({ - setup() { - const form = useOmegaForm( - S.Struct({ - rows: S.Array(S.Struct({ text: S.String.pipe(S.check(S.isMinLength(2))) })) - }), - { - defaultValues: { - rows: Array.from({ length: index + 1 }, (_, i) => ({ text: i === index ? "" : "valid" })) - } - } - ) - return { form, name: `rows[${index}].text`, visible: ref(true) } - }, - template: ` - - - - - - - ` - })) - - const input = wrapper.get("input") - await wrapper.vm.form.handleSubmit() - await nextTick() - - expect(wrapper.get("[data-testid=\"field-errors\"]").text()).not.toBe("") - const entries = wrapper.findAll("[role=\"alert\"] .error-item") - expect(entries).toHaveLength(1) - expect(entries[0].get("label").text()).toBe("Row text") - expect(entries[0].get("label").attributes("for")).toBe(input.attributes("id")) - - wrapper.vm.visible = false - await nextTick() - await wrapper.vm.form.handleSubmit() - await nextTick() - - const unregistered = wrapper.findAll("[role=\"alert\"] .error-item") - expect(unregistered).toHaveLength(1) - expect(unregistered[0].get("label").attributes("for")).toBe(`rows.${index}.text`) - wrapper.unmount() - }) - - it("still resolves indexed metadata inside form.Array", () => { - const wrapper = mount(defineComponent({ - setup() { - return { - form: useOmegaForm(S.Struct({ rows: S.Array(S.Struct({ text: S.String })) }), { - defaultValues: { rows: [{ text: "existing" }] } - }) - } - }, - template: ` - - - - - - ` - })) - - expect(wrapper.get("input").element.value).toBe("existing") - wrapper.unmount() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts b/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts deleted file mode 100644 index 6e8441959..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { defineComponent } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const VTextField = defineComponent({ - props: { - modelValue: { - type: String, - default: "" - } - }, - emits: ["update:modelValue"], - template: ` - - ` -}) - -const VSelect = defineComponent({ - props: { - items: { - type: Array, - default: () => [] - }, - modelValue: { - type: String, - default: "" - } - }, - emits: ["update:modelValue"], - template: ` - - ` -}) - -describe("OmegaForm input attributes", () => { - it("does not forward the internal form object as a native form attribute", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - password: S.NonEmptyString255 - }), - { - defaultValues: { - password: "" - } - } - ) - - return { form } - } - }, { - global: { - components: { VTextField, VSelect } - } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("input") - const nativeForm = wrapper.find("form").element - - expect(input.attributes("form")).toBeUndefined() - expect(input.attributes("validators")).toBeUndefined() - expect(Object.values(input.attributes())).not.toContain("[object Object]") - expect((input.element as HTMLInputElement).form).toBe(nativeForm) - }) - - it("renders a newly-supported native input type (tel) on the text-field branch", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ phone: S.String }), - { defaultValues: { phone: "" } } - ) - - return { form } - } - }, { - global: { - components: { VTextField } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("input").attributes("type")).toBe("tel") - }) - - it("falls back to a text input and warns for a type no branch handles", async () => { - const warn = vi.spyOn(console, "warn").mockImplementation(() => {}) - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ mystery: S.String }), - { defaultValues: { mystery: "" } } - ) - - return { form } - } - }, { - global: { - components: { VTextField } - } - }) - - await wrapper.vm.$nextTick() - - // unhandled type → getInputType maps it to "text" - expect(wrapper.find("input").attributes("type")).toBe("text") - expect(warn).toHaveBeenCalledWith(expect.stringContaining("totally-custom")) - - warn.mockRestore() - }) - - it("keeps input configuration props off the renderer attrs", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - choice: S.Literal("one") - }), - { - defaultValues: { - choice: "one" - } - } - ) - - return { form } - } - }, { - global: { - components: { VTextField, VSelect } - } - }) - - await wrapper.vm.$nextTick() - - const select = wrapper.find("select") - - expect(select.attributes("options")).toBeUndefined() - expect(select.attributes("items")).toBeUndefined() - expect(Object.values(select.attributes())).not.toContain("[object Object]") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts b/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts deleted file mode 100644 index 37b2b4996..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("OmegaForm input reactivity", () => { - it("reflects value changes from the slot `state` in the view", async () => { - const schema = S.Struct({ - name: S.String - }) - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { name: "" } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("[data-testid='input']") - await input.setValue("hello") - await wrapper.vm.$nextTick() - - // The slot `state` must stay in sync with the form store - expect(wrapper.find("[data-testid='display']").text()).toBe("hello") - expect((input.element as HTMLInputElement).value).toBe("hello") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts b/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts deleted file mode 100644 index 822fd6555..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { createUseFormWithCustomInput, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const VTextField = defineComponent({ - props: { modelValue: { type: String, default: "" } }, - emits: ["update:modelValue"], - template: `` -}) - -const RatingInput = defineComponent({ - props: { - inputProps: { type: Object, required: true }, - field: { type: Object, required: true }, - state: { type: Object, required: true } - }, - template: `
{{ inputProps.label }}
` -}) - -const UniversalInput = defineComponent({ - props: { - inputProps: { type: Object, required: true }, - field: { type: Object, required: true }, - state: { type: Object, required: true } - }, - template: `
{{ inputProps.label }}
` -}) - -const mountWith = (omegaConfig: any, type: string, name = "x") => - mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - `, - setup() { - const form = useOmegaForm(S.Struct({ x: S.String }), { defaultValues: { x: "" } }, omegaConfig) - return { form } - } - }, { global: { components: { VTextField } } }) - -describe("OmegaForm input registry", () => { - it("renders the registered component for a custom type", async () => { - const wrapper = mountWith({ inputs: { rating: RatingInput } }, "rating") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - }) - - it("overrides a built-in type when registered", async () => { - const wrapper = mountWith({ inputs: { string: RatingInput } }, "string") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - }) - - it("falls back to the built-in renderer when no entry matches", async () => { - const wrapper = mountWith({ inputs: { rating: RatingInput } }, "string") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(false) - expect(wrapper.find("input").exists()).toBe(true) - }) -}) - -describe("createUseFormWithCustomInput + inputs", () => { - it("uses a per-type registered component, else the universal custom input", async () => { - const useForm = createUseFormWithCustomInput(UniversalInput) - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm( - S.Struct({ score: S.Number, nickname: S.String }), - { defaultValues: { score: 0, nickname: "" } }, - { inputs: { rating: RatingInput } } - ) - return { form } - } - }, { global: { components: { VTextField } } }) - await wrapper.vm.$nextTick() - // `score` (type="rating") resolves the registered component... - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - // ...while `nickname` falls back to the universal custom input. - expect(wrapper.find("[data-testid=\"universal\"]").exists()).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts b/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts deleted file mode 100644 index 00e3ba9a5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { flushPromises, mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { beforeEach, describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// Mock components that integrate with TanStack Form -const mockComponents = { - CustomInput: { - template: ` -
- - - -
- `, - props: ["form", "name", "field", "label", "type", "options"], - inheritAttrs: false - }, - OmegaErrors: { - template: `
{{ JSON.stringify(errors) }}
`, - props: ["errors", "generalErrors", "showErrors"] - } -} - -describe("OmegaForm Intersection/Union", () => { - const AlphaSchema = S.Struct({ - first: S.Literal("alpha"), - alpha: S.NonEmptyString - }) - - const BetaSchema = S.Struct({ - first: S.Literal("beta"), - beta: S.NonEmptyString - }) - - const MySchema = S.Struct({ - myUnion: S.Union([AlphaSchema, BetaSchema]) - }) - - const createWrapper = () => - mount({ - components: { - OmegaIntlProvider, - CustomInput: mockComponents.CustomInput - }, - template: ` - - - - - - `, - setup() { - let submittedValue: any = null - const form = useOmegaForm(MySchema, { - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { - stubs: { - CustomInput: mockComponents.CustomInput, - OmegaErrorsInternal: mockComponents.OmegaErrors - } - } - }) - - let wrapper: ReturnType - - beforeEach(() => { - wrapper = createWrapper() - }) - - it("handles discriminated union with conditional fields", async () => { - // Initially, neither alpha nor beta input should be visible - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(false) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(false) - - // Select "alpha" from the dropdown - const selectInput = wrapper.find("select[name='myUnion.first']") - await selectInput.setValue("alpha") - await wrapper.vm.$nextTick() - - // Alpha input should now be visible, beta should not - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(true) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(false) - - // Switch to "beta" - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - // Beta input should now be visible, alpha should not - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(false) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(true) - }) - - it("clears validation errors when switching union types", async () => { - const selectInput = wrapper.find("select[name='myUnion.first']") - - // Select "alpha" and submit without filling required field to trigger error - await selectInput.setValue("alpha") - - const submitButton = wrapper.find("[data-testid='submit']") - await submitButton.trigger("submit") - await wrapper.vm.$nextTick() - await flushPromises() - - // Verify error exists for alpha field - const errorDiv = wrapper.find(".mock-omega-errors") - expect(errorDiv.exists()).toBe(true) - expect(errorDiv.text()).toContain("alpha") - - // Switch to "beta" - error should be cleared - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - const errorDivAfter = wrapper.find(".mock-omega-errors") - expect(errorDivAfter.text()).not.toContain("alpha") - }) - - it("clears form values when switching union types and handles submission", async () => { - const selectInput = wrapper.find("select[name='myUnion.first']") - - // Select "alpha" and fill the input - await selectInput.setValue("alpha") - await wrapper.vm.$nextTick() - - const alphaInput = wrapper.find("input[name='myUnion.alpha']") - await alphaInput.setValue("test") - await wrapper.vm.$nextTick() - - // Check that "test" is in the values - const valuesText = wrapper.find("[data-testid='values']").text() - expect(valuesText).toContain("test") - - // Switch to "beta" - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - // // Check that alpha "test" value is not there anymore - // const valuesAfterSwitch = wrapper.find("[data-testid='values']").text() - // expect(valuesAfterSwitch).not.toContain("test") - - // Fill beta with "here we go" - const betaInput = wrapper.find("input[name='myUnion.beta']") - await betaInput.setValue("here we go") - await wrapper.vm.$nextTick() - - // Click submit and check submission works - const form = wrapper.find("form") - await form.trigger("submit") - await wrapper.vm.$nextTick() - // Wait a bit for async operations - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify the form data was submitted with the correct beta values - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ - myUnion: { - first: "beta", - beta: "here we go" - } - }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/Meta.test.ts b/packages/vue-components/__tests__/OmegaForm/Meta.test.ts deleted file mode 100644 index 0ed85b1b1..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Meta.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// __tests__/OmegaForm/Meta.test.ts -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -const subStruct = { - a: S.NullOr(S.String), - b: S.UndefinedOr(S.Finite), - c: S.NullishOr(S.Finite), - d: S.String, - e: S.Finite, - f: S.Boolean -} - -const schema = S.Struct({ - ...subStruct, - struct: S.Struct(subStruct), - nullableStruct: S.NullOr(S.Struct(subStruct)) -}) - -const expectedSubMeta = { - a: { required: false, nullableOrUndefined: "null", type: "string" }, - b: { required: false, nullableOrUndefined: "undefined", type: "number" }, - c: { required: false, nullableOrUndefined: "undefined", type: "number" }, - d: { type: "string", required: false, nullableOrUndefined: false }, - e: { type: "number", required: true, nullableOrUndefined: false }, - f: { type: "boolean", required: true, nullableOrUndefined: false } -} - -describe("Meta story characterization", () => { - it("matches the rendered meta keys and values", () => { - const { meta } = generateMetaFromSchema(schema) - - for (const [key, value] of Object.entries(expectedSubMeta)) { - expect(meta[key as keyof typeof meta], `root.${key}`).toMatchObject(value) - expect(meta[`struct.${key}` as keyof typeof meta], `struct.${key}`).toMatchObject(value) - expect(meta[`nullableStruct.${key}` as keyof typeof meta], `nullableStruct.${key}`).toMatchObject(value) - } - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts b/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts deleted file mode 100644 index a59acb468..000000000 --- a/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Nullable field initialization", () => { - it("should initialize nullable fields with null when missing from defaultValues", async () => { - const schema = S.Struct({ - aString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - bString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - cStruct: S - .NullOr(S.Struct({ - dString: S.NullOr(S.NonEmptyString255).withConstructorDefault - })) - .withConstructorDefault, - cLiteral: S.NullOr(S.Literal("test", "test2")).withConstructorDefault - }) - - let submittedValue: Record | null = null - let submitError = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - aString: null - }, - onSubmit: async ({ value }) => { - try { - submittedValue = value - } catch (error) { - submitError = error - } - } - }) - return { form } - } - }) - - // Check that both fields are initialized with null (not empty string) - await vi.waitFor(() => { - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"aString\":null") - expect(valuesText).toContain("\"bString\":null") - expect(valuesText).toContain("\"cStruct\":null") - }) - - // Submit the form - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - // Wait for the submission to complete - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - // Check that form submitted successfully without errors - expect(submitError).toBeNull() - expect(submittedValue).toEqual({ - aString: null, - bString: null, - cStruct: null, - cLiteral: null - }) - }) - - it("should convert empty string to null for nullable fields", async () => { - const schema = S.Struct({ - aString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - bString: S.NonEmptyString255 - }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async () => { - // Should not be called since bString is required and missing - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - // aString should be null (nullable field with empty string) - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"aString\":null") - - // Submit the form - should fail because bString is required - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - - // Check that there's an error for bString - const errors = wrapper.find("[data-testid=\"omega-errors\"]") - if (errors.exists()) { - expect(errors.text()).toContain("bString") - } - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts deleted file mode 100644 index 7e8b487f3..000000000 --- a/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -/** - * Regression repro for the configurator `LinkedOption.override` schema. - * - * `override` is a nullable struct (`S.NullOr(S.Struct({...})).withConstructorDefault`) - * whose own children are themselves nullable (`S.NullOr(...)`). The form starts - * with `override === null`. As soon as the user fills ONE child (`override.min`), - * the struct materialises while its siblings (`override.max`, `override.readOnly`) - * are still `undefined`. - * - * `undefined` is not a valid member of `S.NullOr(...)`, so validation rejected the - * untouched siblings with "field must not be empty" and the form couldn't submit. - * The configurator worked around this by making every child `S.optional`; the - * correct behaviour is for OmegaForm to treat an untouched nullable child as - * `null` once its nullable parent struct materialises. - */ -describe("Nullable nested struct validation", () => { - const schema = S.Struct({ - optionId: S.NullOr(S.String), - override: S - .NullOr(S.Struct({ - min: S.NullOr(S.NonNegativeNumber), - max: S.NullOr(S.NonNegativeNumber), - readOnly: S.NullOr(S.Boolean).withConstructorDefault, - isInteger: S.optional(S.NullOr(S.Boolean)) - })) - .withConstructorDefault - }) - - it("submits when only one child of a nullable struct is filled", async () => { - let submittedValue: Record | null = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async ({ value }) => { - submittedValue = value as Record - } - }) - return { form } - } - }) - - // The whole struct starts null. - await vi.waitFor(() => { - expect(wrapper.find("[data-testid=\"values\"]").text()).toContain("\"override\":null") - }) - - // User fills only `override.min`. - await wrapper.find("[data-testid=\"min-input\"]").setValue("100") - - // The struct materialised: its untouched nullable siblings are backfilled - // into the live form state, not left `undefined`. - await vi.waitFor(() => { - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"min\":100") - expect(valuesText).toContain("\"max\":null") - expect(valuesText).toContain("\"readOnly\":null") - }) - - // Submit. - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - // The untouched nullable siblings must NOT block submission; they decode as null. - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - expect(submittedValue).toMatchObject({ - override: { - min: 100, - max: null, - readOnly: null - } - }) - }) - - it("keeps an untouched nullable struct as null", async () => { - let submittedValue: Record | null = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async ({ value }) => { - submittedValue = value as Record - } - }) - return { form } - } - }) - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - // Nothing was filled — `override` must stay null, not materialise. - expect(submittedValue).toMatchObject({ override: null }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts b/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts deleted file mode 100644 index 2884bfb4c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import * as Order from "effect/Order" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// We need to mock the components used by OmegaAutoGen.vue since they depend on Vuetify -const mockComponents = { - OmegaInput: { - template: ` -
- - -
- `, - props: ["form", "name", "label"] - } -} - -describe("OmegaAutoGen", () => { - it("generates components from schema with the correct props", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(2) // Should generate 2 inputs - - const firstInput = inputs[0] - expect(firstInput.find("label").attributes("for")).toBe("string") - expect(firstInput.find("input").attributes("name")).toBe("string") - - const secondInput = inputs[1] - expect(secondInput.find("label").attributes("for")).toBe("number") - expect(secondInput.find("input").attributes("name")).toBe("number") - }) - - // Pick option test - it("uses the pick option to include only specific fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Should only have inputs for fields in the pick array - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(1) - expect(inputs[0].find("input").attributes("name")).toBe("string") - }) - - // Omit option test - it("uses the omit option to exclude specific fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Should only have inputs for fields not in the omit array - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(1) - expect(inputs[0].find("input").attributes("name")).toBe("string") - }) - - // Order option test - it("uses the order option to control field order", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Check that the fields are ordered according to the specified order - const inputs = wrapper.findAll(".mock-omega-input input") - const inputNames = inputs.map((input) => input.attributes("name")) - - // The order should match the specified order - expect(inputNames[0]).toBe("boolean") - expect(inputNames[1]).toBe("number") - expect(inputNames[2]).toBe("string") - }) - - // Sort option test - it("uses the sort option to sort fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - charlie: S.String, - alpha: S.String, - bravo: S.String - }) - ) - - // Create a sorter that sorts by name alphabetically - const sortByName = Order.mapInput( - Order.String, - (item: { name: string }) => item.name - ) - - return { form, sortByName } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Check that the fields are sorted alphabetically by name - const inputs = wrapper.findAll(".mock-omega-input input") - const inputNames = inputs.map((input) => input.attributes("name")) - - // The fields should be sorted alphabetically - expect(inputNames[0]).toBe("alpha") - expect(inputNames[1]).toBe("bravo") - expect(inputNames[2]).toBe("charlie") - }) - - // LabelMap - it("applies the labelMap option", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - const labels = wrapper.findAll("label") - expect(labels[0].text()).toBe("Custom String Label") - expect(labels[1].text()).toBe("number") // Default label is the key - }) - - // Custom slot - it("allows custom slot content", () => { - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }) - - const customElements = wrapper.findAll(".custom-slot") - expect(customElements.length).toBe(2) - - expect(wrapper.find("[data-testid=\"string-custom\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"number-custom\"]").exists()).toBe(true) - - const labels = wrapper.findAll("label") - expect(labels[0].text()).toBe("Custom string") - expect(labels[1].text()).toBe("Custom number") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts b/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts deleted file mode 100644 index 03363ab36..000000000 --- a/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -describe("optionalKey required handling", () => { - it("should mark optionalKey fields as not required", () => { - const schema = S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - - const { meta } = generateMetaFromSchema(schema) - - // optionalKey field should NOT be required - expect(meta.number?.required).toBe(false) - - // regular field should still be required - expect(meta.height?.required).toBe(true) - }) - - it("should mark optionalKey with decodingDefault as not required", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))), - age: S.NonEmptyString255 - }) - - const { meta } = generateMetaFromSchema(schema) - - expect(meta.name?.required).toBe(false) - expect(meta.age?.required).toBe(true) - }) - - it("should handle optionalKey in tagged union branches", () => { - const schema = S.Union([ - S.TaggedStruct("one", { - a: S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))), - z: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - }), - S.TaggedStruct("two", { - a: S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))), - y: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - }) - ]) - - const { meta } = generateMetaFromSchema(schema) - - // optionalKey in both union branches should not be required - expect(meta["a.number"]?.required).toBe(false) - - // regular fields should be required - expect(meta["a.height"]?.required).toBe(true) - expect(meta["a.z"]?.required).toBe(true) - expect(meta["a.y"]?.required).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts b/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts deleted file mode 100644 index e2d46c0a5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as S from "effect-app/Schema" -import * as Redacted from "effect/Redacted" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, toFormSchema } from "../../src/components/OmegaForm" - -describe("S.Redacted meta generation", () => { - it("should generate correct meta for S.Redacted(S.NonEmptyString255)", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["email"]).toMatchObject({ - type: "string", - minLength: 1, - maxLength: 255, - required: true, - nullableOrUndefined: false - }) - - expect(result.meta["password"]).toMatchObject({ - type: "string", - minLength: 1, - maxLength: 255, - required: true, - nullableOrUndefined: false - }) - }) - - it("should generate correct meta for S.Redacted(S.String) — not required since empty string is valid", () => { - const schema = S.Struct({ - secret: S.Redacted(S.String) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["secret"]).toMatchObject({ - type: "string", - required: false, - nullableOrUndefined: false - }) - }) - - it("should generate correct meta for S.NullOr(S.Redacted(S.NonEmptyString255))", () => { - const schema = S.Struct({ - optionalSecret: S.NullOr(S.Redacted(S.NonEmptyString255)) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["optionalSecret"]).toMatchObject({ - type: "string", - required: false, - nullableOrUndefined: "null" - }) - }) -}) - -describe("toFormSchema — S.Redacted validation", () => { - it("should accept plain strings for S.Redacted fields and decode to Redacted", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - const result = S.decodeUnknownSync(formSchema)({ - email: "test@test.com", - password: "secret123" - }) - - expect(result.email).toBe("test@test.com") - expect(Redacted.isRedacted(result.password)).toBe(true) - expect(Redacted.value(result.password as Redacted.Redacted)).toBe("secret123") - }) - - it("should accept plain strings for S.Redacted(S.String)", () => { - const schema = S.Struct({ - secret: S.Redacted(S.String) - }) - - const formSchema = toFormSchema(schema) - const result = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - - expect(Redacted.isRedacted(result.secret)).toBe(true) - }) - - it("should pass standard schema validation with plain string values", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - const ssv = S.toStandardSchemaV1(formSchema) - const result = (ssv as any)["~standard"].validate({ - email: "test@test.com", - password: "secret123" - }) - - expect(result.issues).toBeUndefined() - }) - - it("should still reject invalid inner values", () => { - const schema = S.Struct({ - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - expect(() => S.decodeUnknownSync(formSchema)({ password: "" })).toThrow() - }) - - it("should handle NullOr(S.Redacted(...))", () => { - const schema = S.Struct({ - secret: S.NullOr(S.Redacted(S.NonEmptyString255)) - }) - - const formSchema = toFormSchema(schema) - - const result1 = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(Redacted.isRedacted(result1.secret)).toBe(true) - - const result2 = S.decodeUnknownSync(formSchema)({ secret: null }) - expect(result2.secret).toBeNull() - }) - - it("should return the original schema when no S.Redacted fields exist", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - name: S.String - }) - - const formSchema = toFormSchema(schema) - expect(formSchema).toBe(schema) - }) - - it("should work with S.Class schemas", () => { - class Credentials extends S.Class("Credentials")({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) {} - - const formSchema = toFormSchema(Credentials) - const result = S.decodeUnknownSync(formSchema)({ - email: "test@test.com", - password: "secret" - }) - - expect(Redacted.isRedacted(result.password)).toBe(true) - }) - - it("toFormSchema rewrites NullOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.NullOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - // formSchema must accept a plain string (encoded side) for the secret field - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) - - it("toFormSchema rewrites UndefinedOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.UndefinedOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) - - it("toFormSchema rewrites NullishOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.NullishOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts b/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts deleted file mode 100644 index 542a0b302..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -// __tests__/OmegaForm/SelectMultipleLocalization.test.ts -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const mountForm = (setup: () => T): T => { - let captured: T | undefined - const Inner = defineComponent({ - setup() { - captured = setup() - return {} - }, - template: "
" - }) - mount(defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - })) - if (!captured) throw new Error("no capture") - return captured -} - -const firstFieldError = (form: any, name: string): string | undefined => { - const errs = form.getAllErrors().fields[name]?.errors - if (!errs) return undefined - const flat = errs.flat ? errs.flat() : errs - return flat[0]?.message -} - -describe("select / multiple validation messages carry localized payload", () => { - it("S.Literals — invalid (non-empty) member emits 'select' message with members", async () => { - const schema = S.Struct({ color: S.Literals(["red", "blue", "green"]) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - // OmegaIntlProvider's mock `trans` returns `${key}_translated` and - // ignores variables — so we just need the i18n key to flow, not the - // substituted variables. - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.Array(S.Literals) — non-array value emits 'multiple' message", async () => { - const schema = S.Struct({ tags: S.Array(S.Literals(["a", "b", "c"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { tags: "not-an-array" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "tags") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.NullOr(S.Literals) — invalid member emits localized select message", async () => { - const schema = S.Struct({ color: S.NullOr(S.Literals(["red", "blue", "green"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.UndefinedOr(S.Literals) — invalid member emits localized select message", async () => { - const schema = S.Struct({ color: S.UndefinedOr(S.Literals(["red", "blue", "green"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("user-supplied .annotate({ message }) on S.Literals is preserved", async () => { - const schema = S.Struct({ - color: S.Literals(["red", "blue", "green"]).annotate({ - message: "user.custom.message" - }) - }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - // User annotation wins — verbatim, not piped through trans. - expect(msg).toBe("user.custom.message") - }) - - it("empty value on a required select also emits the localized 'select' message (matches main)", async () => { - // Main's `generateInputStandardSchemaFromFieldMeta` annotated the entire - // S.Literals schema with a `validation.not_a_valid` message, so any - // failure (including undefined) surfaced it. We preserve that behavior. - const schema = S.Struct({ color: S.Literals(["red", "blue", "green"]) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: undefined } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg).toBe("validation.not_a_valid_translated") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts b/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts deleted file mode 100644 index 2c4fd11fb..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -// __tests__/OmegaForm/SubmitEffect.test.ts -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import * as Cause from "effect/Cause" -import * as Exit from "effect/Exit" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { FormErrors, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const mountForm = (setupForm: () => T): T => { - let captured: T | undefined - const Inner = defineComponent({ - setup() { - captured = setupForm() - return {} - }, - template: "
" - }) - const Wrapper = defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - }) - mount(Wrapper) - if (!captured) throw new Error("setupForm did not return") - return captured -} - -describe("handleSubmitEffect", () => { - it("succeeds with void on valid input (no checkErrors)", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "ok" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise(Effect.exit(form.handleSubmitEffect())) - expect(Exit.isSuccess(exit)).toBe(true) - }) - - it("succeeds with void even on invalid input without checkErrors", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise(Effect.exit(form.handleSubmitEffect())) - expect(Exit.isSuccess(exit)).toBe(true) - }) - - it("fails with FormErrors when checkErrors and validation fails", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise( - Effect.exit(form.handleSubmitEffect({ checkErrors: true })) - ) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) { - const failReason = exit.cause.reasons.find(Cause.isFailReason) - expect(failReason?.error).toBeInstanceOf(FormErrors) - } - }) - - it("delivers decoded To to onSubmit (not raw From)", async () => { - let received: unknown - const form = mountForm(() => - useOmegaForm( - S.Struct({ n: S.FiniteFromString }), - { - defaultValues: { n: "42" }, - onSubmit: async ({ value }) => { - received = value - } - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(received).toEqual({ n: 42 }) - }) - - it("awaits Promise-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: async () => { - await new Promise((r) => setTimeout(r, 5)) - resolved = true - } - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) - - it("awaits Effect-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: () => - Effect.sync(() => { - resolved = true - }) - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) - - it("awaits Fiber-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: () => - Effect.runFork( - Effect.sync(() => { - resolved = true - }) - ) - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts b/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts deleted file mode 100644 index 7d4bb7200..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -// __tests__/OmegaForm/SubmitErrorClear.test.ts -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("post-submit revalidation (TanStack default)", () => { - it("revalidates the form-level schema on value change after a failed submit", async () => { - const schema = S.Struct({ - a: S.String.pipe(S.check(S.isMinLength(2))), - b: S.String.pipe(S.check(S.isMinLength(2))) - }) - - let captured: ReturnType> | undefined - const Inner = defineComponent({ - setup() { - const form = useOmegaForm(schema, { defaultValues: { a: "", b: "" } }) - captured = form as any - return { form } - }, - // Render fields so they register with the form. Without mounted fields, - // setFieldValue cannot trigger validation (FormApi.validateField bails - // when fieldInfo[name].instance is undefined). - template: ` - - - - - ` - }) - const Wrapper = defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - }) - mount(Wrapper) - if (!captured) throw new Error("setupForm did not return") - const form = captured - - await form.handleSubmit() - await nextTick() - - // After failed submit, fix only field a; field b should still fail validation - // because TanStack revalidates the entire form-level schema. - form.setFieldValue("a", "ok") - await nextTick() - await nextTick() - - // canSubmit should still be false because b is still invalid - expect(form.store.state.canSubmit).toBe(false) - - // Now fix b too - form.setFieldValue("b", "ok") - await nextTick() - await nextTick() - expect(form.store.state.canSubmit).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts b/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts deleted file mode 100644 index 7e229904e..000000000 --- a/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick } from "vue" -import { createUseFormWithCustomInput } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const HeadlessInput = defineComponent({ - inheritAttrs: false, - props: ["inputProps", "field", "state", "options"], - template: ` -
- - - {{ inputProps.errorMessages?.join(', ') }} -
- `, - methods: { - onInput(this: any, e: Event) { - const target = e.target as HTMLInputElement - const value = target.value - if (this.inputProps.type === "number") { - this.field.handleChange(value === "" ? null : Number(value)) - } else { - this.field.handleChange(value) - } - } - } -}) - -const useForm = createUseFormWithCustomInput(HeadlessInput) - -const flushAfterTagChange = async () => { - // OmegaTaggedUnionInternal watches _tag and on change calls form.reset(), - // then setTimeout(validate, 0). Give it room. - await nextTick() - await nextTick() - await new Promise((r) => setTimeout(r, 10)) - await nextTick() -} - -describe("RootLevelTaggedUnion behavior", () => { - const schema = S.Union([ - S.TaggedStruct("A", { - a: S.NonEmptyString255.pipe( - S.withConstructorDefault(Effect.succeed(S.NonEmptyString255("aaaa"))) - ), - common: S.NonEmptyString255 - }), - S.TaggedStruct("B", { - b: S.Finite, - nullableB: S.NullOr(S.Finite), - common: S.NullOr(S.String) - }) - ]) - - const mountForm = (onSubmit?: (value: any) => void) => { - return mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm(schema as any, { - defaultValues: { _tag: "A" } as any, - onSubmit: async ({ value }: any) => onSubmit?.(value) - }) - const tagOptions = [ - { value: "A", title: "Option A" }, - { value: "B", title: "Option B" } - ] - return { form, tagOptions } - } - }) - } - - it("renders branch A's slot by default and not B's", async () => { - const wrapper = mountForm() - await nextTick() - - expect(wrapper.find("[data-testid=\"input-a\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"input-common\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"input-b\"]").exists()).toBe(false) - expect(wrapper.find("[data-testid=\"input-nullableB\"]").exists()).toBe(false) - }) - - it("toggling _tag from A to B swaps the rendered branch slot", async () => { - const wrapper = mountForm() - await nextTick() - - const select = wrapper.find("[data-testid=\"select-_tag\"]") - expect(select.exists()).toBe(true) // Drive the change through the form API to mirror what the UI handler - // produces. setValue + change events on the bare - {{ inputProps.errorMessages.join(', ') }} - - `, - props: ["inputProps", "field"], - inheritAttrs: false -} - -describe("createUseFormWithCustomInput", () => { - const useForm = createUseFormWithCustomInput(CustomTestInput) - const schema = S.Struct({ testField: S.String }) - - const createWrapper = (options?: { - defaultValues?: any - onSubmit?: (data: any) => void | Promise - }) => { - let submittedValue: any = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useForm(schema, { - defaultValues: options?.defaultValues || { testField: "" }, - onSubmit: async ({ value }) => { - submittedValue = value - options?.onSubmit?.(value) - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { - stubs: { - CustomTestInput - } - } - }) - - return wrapper - } - - it("should render custom input component with correct props", async () => { - const wrapper = createWrapper({ defaultValues: { testField: "test value" } }) - await wrapper.vm.$nextTick() - - // Check that custom input was rendered - const customInput = wrapper.find("[data-testid=\"custom-input\"]") - expect(customInput.exists()).toBe(true) - expect(customInput.classes()).toContain("custom-test-input") - - // Verify the input has the correct value - const inputElement = wrapper.find("[data-testid=\"custom-input-field\"]") - expect(inputElement.exists()).toBe(true) - expect((inputElement.element as HTMLInputElement).value).toBe("test value") - - // Verify the label is rendered - const label = customInput.find("label") - expect(label.exists()).toBe(true) - expect(label.text()).toContain("Test Field") - }) - - it("should handle input changes and submission", async () => { - const wrapper = createWrapper({ defaultValues: { testField: "initial" } }) - await wrapper.vm.$nextTick() - - // Verify initial value - const input = wrapper.find("[data-testid=\"custom-input-field\"]") - expect((input.element as HTMLInputElement).value).toBe("initial") - - // Change the value - await input.setValue("changed value") - await wrapper.vm.$nextTick() - - // Verify the value changed in the form state - const valuesDiv = wrapper.find("[data-testid=\"values\"]") - expect(valuesDiv.text()).toContain("\"testField\":\"changed value\"") - - // Submit the form - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Check that the form received the changed value - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ testField: "changed value" }) - }) - - it("should pass inputProps to custom component", async () => { - // Custom component that validates props - const PropsValidatingInput = { - template: ` -
-
{{ !!inputProps }}
-
{{ !!field }}
-
{{ inputProps.label }}
-
{{ field.name }}
-
{{ inputProps.id }}
-
- `, - props: ["inputProps", "field"] - } - - const useFormWithValidator = createUseFormWithCustomInput(PropsValidatingInput) - const validationSchema = S.Struct({ myField: S.String }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithValidator(validationSchema, { - defaultValues: { myField: "test-value" } - }) - return { form } - } - }, { - global: { - stubs: { - PropsValidatingInput - } - } - }) - - await wrapper.vm.$nextTick() - - const validator = wrapper.find("[data-testid=\"props-validator\"]") - expect(validator.exists()).toBe(true) - - // Check all required props are passed - expect(wrapper.find("[data-testid=\"has-inputprops\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"has-field\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"has-label\"]").text()).toContain("My Label") - expect(wrapper.find("[data-testid=\"has-id\"]").text()).toBeTruthy() - }) - - it("forwards an explicit `required` override to inputProps (survives OmegaInput's prop whitelist)", async () => { - const RequiredProbe = { - template: `
{{ inputProps.required }}
`, - props: ["inputProps", "field"] - } - - const useFormWithProbe = createUseFormWithCustomInput(RequiredProbe) - // optionalKey field => meta.required is false, so a `true` result proves the - // explicit prop overrode meta AND was not dropped on the way to the input. - const optionalSchema = S.Struct({ maybe: S.optionalKey(S.Number) }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithProbe(optionalSchema, { defaultValues: {} }) - return { form } - } - }, { - global: { - stubs: { - RequiredProbe - } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"required-probe\"]").text()).toBe("true") - }) - - it("falls back to schema-derived required when the prop is omitted", async () => { - const RequiredProbe = { - template: ` -
-
{{ inputProps.required }}
-
{{ inputProps.label }}
-
- `, - props: ["inputProps", "field"] - } - - const useFormWithProbe = createUseFormWithCustomInput(RequiredProbe) - const requiredSchema = S.Struct({ number: S.Number }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithProbe(requiredSchema) - return { form } - } - }, { - global: { - stubs: { - RequiredProbe - } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"required-probe\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"label-probe\"]").text()).toBe("Number *") - }) - - it("should pass event listeners to custom input component", async () => { - // Custom component that emits focus and blur events - const EventEmittingInput = { - template: ` -
- -
- `, - props: ["inputProps", "field"], - emits: ["focus", "blur"], - inheritAttrs: false - } - - const useFormWithEvents = createUseFormWithCustomInput(EventEmittingInput) - const eventSchema = S.Struct({ eventField: S.String }) - - let focusCount = 0 - let blurCount = 0 - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithEvents(eventSchema, { - defaultValues: { eventField: "" } - }) - - const handleFocus = () => { - focusCount++ - } - - const handleBlur = () => { - blurCount++ - } - - return { form, handleFocus, handleBlur } - } - }, { - global: { - stubs: { - EventEmittingInput - } - } - }) - - await wrapper.vm.$nextTick() - - // Verify the custom input was rendered - const eventInput = wrapper.find("[data-testid=\"event-input\"]") - expect(eventInput.exists()).toBe(true) - - // Trigger focus event - await eventInput.trigger("focusin") - await wrapper.vm.$nextTick() - - // Verify focus handler was called - expect(focusCount).toBe(1) - - // Trigger blur event - await eventInput.trigger("focusout") - await wrapper.vm.$nextTick() - - // Verify blur handler was called - expect(blurCount).toBe(1) - - // Trigger multiple times to ensure it keeps working - await eventInput.trigger("focusin") - await eventInput.trigger("focusout") - await wrapper.vm.$nextTick() - - expect(focusCount).toBe(2) - expect(blurCount).toBe(2) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaFormTest.vue b/packages/vue-components/__tests__/OmegaFormTest.vue deleted file mode 100644 index b84195d3f..000000000 --- a/packages/vue-components/__tests__/OmegaFormTest.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - diff --git a/packages/vue-components/__tests__/OmegaIntlProvider.vue b/packages/vue-components/__tests__/OmegaIntlProvider.vue deleted file mode 100644 index bfcf31cbc..000000000 --- a/packages/vue-components/__tests__/OmegaIntlProvider.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts b/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts deleted file mode 100644 index ad5f1393c..000000000 --- a/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts +++ /dev/null @@ -1,703 +0,0 @@ -/** - * OmegaForm Schema Tests - * - * These tests pin the current behavior of generateMetaFromSchema and defaultsValueFromSchema, - * ensuring regression safety during refactors. - * - * Strategy: - * 1. "current" tests → pin behavior with current schema patterns - * 2. When refactoring internals, ALL tests must stay green - * - * NOTE: Uses `S.withConstructorDefault` from effect-app (wraps in Effect), - * NOT `S.withConstructorDefault` from native effect (expects raw Option return). - * - * ---- v4 breaking changes (from v3) ---- - * - * 1. Nested union _tag: FIXED — both patterns now produce "select" - * S.TaggedStruct produces a bare Literal AST node. - * Legacy S.Struct({ _tag: S.Literal("X") }) produces Union([Literal("X")]) - * after AST.toType. Fixed via unwrapSingleLiteralUnion helper. - * A console.warn is emitted for the legacy pattern to encourage migration. - * - * 2. Root-level union unionMeta: FIXED — both patterns now generate unionMeta - * Same root cause as #1: single-element Union wrapping prevented tag extraction. - * Fixed via the same unwrapSingleLiteralUnion helper in metadataFromAst. - * - * 3. UndefinedOr defaults include the key with explicit undefined - * v3: defaultsValueFromSchema skips keys where the recursive call returns - * undefined (if fieldValue !== undefined), so UndefinedOr fields are - * omitted from the result object entirely. - * v4: the key will be present in the result with an explicit undefined value. - * - * 4. S.optionalKey(X).pipe(S.withDecodingDefault(...)) support in defaultsValueFromSchema - * v3: not supported (v3 equivalent was S.optionalWith which encoded defaults - * inside a PropertySignatureTransformation, opaque to AST inspection). - * v4: defaultsValueFromSchema detects PropertySignatureTransformation - * and extracts defaults, enabling withDecodingDefault as a new pattern for - * declaring field defaults directly on the schema. - * - * ---- API mapping ---- - * - * | v4 | v3 | - * |--------------------------------------------|--------------------------------| - * | `S.check(S.isGreaterThanOrEqualTo(x))` | `S.greaterThanOrEqualTo(x)` | - * | `S.check(S.isLessThanOrEqualTo(x))` | `S.lessThanOrEqualTo(x)` | - * | `S.check(S.isGreaterThan(x))` | `S.greaterThan(x)` | - * | `S.check(S.isLessThan(x))` | `S.lessThan(x)` | - * | `S.check(S.isBetween({minimum, maximum}))` | `S.between(minimum, maximum)` | - * | `S.check(S.isInt())` | `S.int()` | - * | `S.check(S.isMinLength(x))` | `S.minLength(x)` | - * | `S.check(S.isMaxLength(x))` | `S.maxLength(x)` | - * | `S.Union([...members])` | `S.Union(...members)` | - * | `S.optionalKey(S.X)` | `S.optionalWith` | - * | `.pipe(S.withDecodingDefault(...))` | `(S.X, { default: ... })` | - */ - -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defaultsValueFromSchema, generateMetaFromSchema, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** Mount a form and extract its default values */ -const mountAndGetDefaults = async (schema: S.Schema, opts?: any) => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema as any, opts) - return { form } - } - }) - await wrapper.vm.$nextTick() - return JSON.parse(wrapper.find("[data-testid=\"values\"]").text()) -} - -// ============================================================================ -// 1. generateMetaFromSchema — pin current behavior -// ============================================================================ - -describe("generateMetaFromSchema", () => { - describe("simple struct", () => { - const schema = S.Struct({ - name: S.NonEmptyString255, - age: S.Finite.pipe(S.check(S.isGreaterThanOrEqualTo(0))), - active: S.Boolean - }) - - it("produces correct metadata for basic types", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.name?.type).toBe("string") - expect(meta.name?.required).toBe(true) - expect(meta.name?.minLength).toBe(1) - expect(meta.name?.maxLength).toBe(255) - expect(meta.age?.type).toBe("number") - expect(meta.age?.required).toBe(true) - expect(meta.age?.minimum).toBe(0) - expect(meta.active?.type).toBe("boolean") - expect(meta.active?.required).toBe(true) - }) - }) - - describe("nullable fields", () => { - const schema = S.Struct({ - required: S.NonEmptyString255, - nullable: S.NullOr(S.String), - optional: S.UndefinedOr(S.String) - }) - - it("nullable/undefined fields are not required", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.required?.required).toBe(true) - expect(meta.nullable?.required).toBe(false) - expect(meta.nullable?.nullableOrUndefined).toBe("null") - expect(meta.optional?.required).toBe(false) - expect(meta.optional?.nullableOrUndefined).toBe("undefined") - }) - }) - - describe("nested struct", () => { - const schema = S.Struct({ - outer: S.Struct({ - inner: S.NonEmptyString255, - deep: S.Struct({ - value: S.Finite - }) - }) - }) - - it("produces dot-notation keys for nested fields", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["outer.inner"]).toBeDefined() - expect(meta["outer.inner"]?.type).toBe("string") - expect(meta["outer.deep.value"]).toBeDefined() - expect(meta["outer.deep.value"]?.type).toBe("number") - }) - }) - - describe("discriminated union (nested in struct)", () => { - const schema = S.Struct({ - union: S.Union([ - S.Struct({ _tag: S.tag("A"), a: S.NonEmptyString255 }), - S.Struct({ _tag: S.tag("B"), b: S.Finite }) - ]) - }) - - it("generates _tag as select with member values", () => { - const { meta } = generateMetaFromSchema(schema) - // Works for both S.TaggedStruct and legacy S.Struct({ _tag: S.Literal(...) }) - // thanks to single-element Union unwrapping in createMeta - expect(meta["union._tag"]?.type).toBe("select") - expect(meta["union._tag"]?.required).toBe(true) - expect(meta["union._tag"]?.members).toBeDefined() - }) - - it("generates field metadata for each union branch", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["union.a"]?.type).toBe("string") - expect(meta["union.b"]?.type).toBe("number") - }) - - it("nested union now populates unionMeta after unification (Phase 2.4)", () => { - // After unification of root + nested union handling, nested unions also - // populate unionMeta, mirroring root-level behavior. - const { unionMeta } = generateMetaFromSchema(schema) - expect(Object.keys(unionMeta).length).toBe(2) - expect(unionMeta["A"]).toBeDefined() - expect(unionMeta["B"]).toBeDefined() - }) - }) - - describe("nullable discriminated union", () => { - const schema = S.Struct({ - union: S.NullOr( - S.Union([ - S.Struct({ _tag: S.Literal("A"), a: S.NonEmptyString255, common: S.String }), - S.Struct({ _tag: S.Literal("B"), b: S.Finite, common: S.String }) - ]) - ) - }) - - it("_tag is not required, other fields are required based on their type", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["union._tag"]?.required).toBe(false) - expect(meta["union.a"]?.required).toBe(true) - expect(meta["union.b"]?.required).toBe(true) - }) - }) - - describe("root-level discriminated union (legacy Literal pattern)", () => { - const schema = S.Union([ - S.Struct({ _tag: S.Literal("A"), a: S.NonEmptyString255 }), - S.Struct({ _tag: S.Literal("B"), b: S.Finite }) - ]) - - it("generates _tag select and per-member fields with unionMeta", () => { - const { meta, unionMeta } = generateMetaFromSchema(schema) - - expect(meta["_tag"]?.type).toBe("select") - expect(meta["_tag"]?.required).toBe(true) - expect(meta.a?.type).toBe("string") - expect(meta.b?.type).toBe("number") - expect(Object.keys(unionMeta).length).toBe(2) - }) - }) - - describe("root-level discriminated union (TaggedStruct pattern)", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255 }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - it("generates _tag select and per-member fields with unionMeta", () => { - const { meta, unionMeta } = generateMetaFromSchema(schema) - - expect(meta["_tag"]?.type).toBe("select") - expect(meta["_tag"]?.required).toBe(true) - expect(meta["_tag"]?.members).toEqual(["A", "B"]) - expect(meta.a?.type).toBe("string") - expect(meta.b?.type).toBe("number") - expect(Object.keys(unionMeta).length).toBe(2) - expect(unionMeta["A"]).toBeDefined() - expect(unionMeta["B"]).toBeDefined() - }) - - it("unionMeta contains per-branch field metadata", () => { - const { unionMeta } = generateMetaFromSchema(schema) - - expect(unionMeta["A"]?.["a"]?.type).toBe("string") - expect(unionMeta["A"]?.["a"]?.required).toBe(true) - expect(unionMeta["B"]?.["b"]?.type).toBe("number") - expect(unionMeta["B"]?.["b"]?.required).toBe(true) - // Each branch should NOT contain the other branch's fields - expect(unionMeta["A"]?.["b"]).toBeUndefined() - expect(unionMeta["B"]?.["a"]).toBeUndefined() - }) - }) - - describe("literal union (select)", () => { - const schema = S.Struct({ - direction: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]) - }) - - it("generates select type with members", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.direction?.type).toBe("select") - expect(meta.direction?.members).toEqual(["left", "right", "both"]) - }) - }) -}) - -// ============================================================================ -// 2. defaultsValueFromSchema — pin current behavior -// ============================================================================ - -describe("defaultsValueFromSchema", () => { - describe("primitive defaults", () => { - it("String defaults to empty string", () => { - const schema = S.Struct({ name: S.String }) - expect(defaultsValueFromSchema(schema)).toEqual({ name: "" }) - }) - - it("Boolean defaults to false", () => { - const schema = S.Struct({ active: S.Boolean }) - expect(defaultsValueFromSchema(schema)).toEqual({ active: false }) - }) - - it("NullOr defaults to null", () => { - const schema = S.Struct({ value: S.NullOr(S.String) }) - expect(defaultsValueFromSchema(schema)).toEqual({ value: null }) - }) - - it("UndefinedOr defaults include the key with undefined value", () => { - const schema = S.Struct({ value: S.UndefinedOr(S.String) }) - const result = defaultsValueFromSchema(schema) - expect("value" in result).toBe(true) - expect(result.value).toBeUndefined() - }) - }) - - describe("withConstructorDefault (effect-app wrapper)", () => { - it("respects constructor defaults", () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("hello"))), - count: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(42))) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.name).toBe("hello") - expect(defaults.count).toBe(42) - }) - - it("constructor defaults in union members", () => { - const schema = S.Union([ - S.Struct({ - _tag: S.Literal("A").pipe(S.withConstructorDefault(Effect.succeed("A"))), - a: S.String.pipe(S.withConstructorDefault(Effect.succeed("defaultA"))) - }), - S.Struct({ - _tag: S.Literal("B"), - b: S.String - }) - ]) - const defaults = defaultsValueFromSchema(schema) - expect(defaults._tag).toBe("A") - expect(defaults.a).toBe("defaultA") - }) - }) - - describe("nested struct defaults", () => { - it("recursively extracts defaults from nested structs", () => { - const schema = S.Struct({ - outer: S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("nested"))), - value: S.Boolean - }) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.outer.name).toBe("nested") - expect(defaults.outer.value).toBe(false) - }) - }) - - describe("NullOr struct defaults", () => { - it("NullOr struct defaults to null", () => { - const schema = S.Struct({ - nested: S.NullOr(S.Struct({ x: S.String })) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ nested: null }) - }) - }) -}) - -// ============================================================================ -// 3. withDecodingDefault — decoding defaults -// ============================================================================ - -describe("withDecodingDefault decoding defaults", () => { - it("withDecodingDefault fills value during decoding", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(0))) - }) - - const decoded = S.decodeUnknownSync(schema)({}) - expect(decoded).toEqual({ name: "defaultName", age: 0 }) - }) - - it("withDecodingDefault respects provided values", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))) - }) - - const decoded = S.decodeUnknownSync(schema)({ name: "provided" }) - expect(decoded).toEqual({ name: "provided" }) - }) - - it("mixed withConstructorDefault and withDecodingDefault", () => { - const schema = S.Struct({ - constructorDefault: S.String.pipe(S.withConstructorDefault(Effect.succeed("fromConstructor"))), - decodingDefault: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("fromDecoding"))) - }) - - // defaultsValueFromSchema should pick up constructor default - const defaults = defaultsValueFromSchema(schema) - expect(defaults.constructorDefault).toBe("fromConstructor") - - // Decoding should pick up decoding default - const decoded = S.decodeUnknownSync(schema)({ constructorDefault: "x" }) - expect(decoded).toEqual({ constructorDefault: "x", decodingDefault: "fromDecoding" }) - }) - - it("defaultsValueFromSchema extracts withDecodingDefault alongside regular fields", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("decodingDefault"))), - required: S.String - }) - - const defaults = defaultsValueFromSchema(schema) - expect(defaults.required).toBe("") - expect(defaults.name).toBe("decodingDefault") - }) - - it("withDecodingDefault can replace manual form default initialization", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("John"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(25))), - active: S.optionalKey(S.Boolean).pipe(S.withDecodingDefault(Effect.succeed(true))) - }) - - // Single decode call produces all defaults — no AST walking needed - const defaults = S.decodeUnknownSync(schema)({}) - expect(defaults).toEqual({ name: "John", age: 25, active: true }) - - // Partial input fills in missing defaults - const partial = S.decodeUnknownSync(schema)({ name: "Dave" }) - expect(partial).toEqual({ name: "Dave", age: 25, active: true }) - }) -}) - -// ============================================================================ -// 4. Form-level integration tests — defaults flow end-to-end -// ============================================================================ - -describe("Form defaults integration", () => { - it("schema defaults flow into form values", async () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("hello"))), - active: S.Boolean, - nullable: S.NullOr(S.String) - }) - - const values = await mountAndGetDefaults(schema) - expect(values.name).toBe("hello") - expect(values.active).toBe(false) - expect(values.nullable).toBe(null) - }) - - it("union defaults flow into form values", async () => { - const schema = S.Union([ - S.Struct({ - _tag: S.Literal("A").pipe(S.withConstructorDefault(Effect.succeed("A"))), - value: S.String.pipe(S.withConstructorDefault(Effect.succeed("default"))) - }), - S.Struct({ - _tag: S.Literal("B"), - value: S.Finite - }) - ]) - - const values = await mountAndGetDefaults(schema) - expect(values._tag).toBe("A") - expect(values.value).toBe("default") - }) - - it("tanstack defaultValues override schema defaults", async () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("fromSchema"))), - age: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))) - }) - - const values = await mountAndGetDefaults(schema, { - defaultValues: { name: "fromTanstack" } - }) - expect(values.name).toBe("fromTanstack") - expect(values.age).toBe(0) - }) -}) - -// ============================================================================ -// 5. Meta generation for number constraints -// ============================================================================ - -describe("number constraint metadata", () => { - it("separate isGreaterThanOrEqualTo + isLessThanOrEqualTo extracts min and max", () => { - const schema = S.Struct({ - value: S.Finite.pipe( - S.check(S.isGreaterThanOrEqualTo(10)), - S.check(S.isLessThanOrEqualTo(20)) - ) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.type).toBe("number") - expect(meta.value?.minimum).toBe(10) - expect(meta.value?.maximum).toBe(20) - }) - - it("isBetween extracts min and max", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.type).toBe("number") - expect(meta.value?.minimum).toBe(10) - expect(meta.value?.maximum).toBe(20) - }) - - it("isInt extracts refinement", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isInt())) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.refinement).toBe("int") - }) - - it("isGreaterThan extracts exclusive minimum", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isGreaterThan(5))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.exclusiveMinimum).toBe(5) - }) - - it("isLessThan extracts exclusive maximum", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isLessThan(100))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.exclusiveMaximum).toBe(100) - }) -}) - -// ============================================================================ -// 6. Meta generation for string constraints -// ============================================================================ - -describe("string constraint metadata", () => { - it("isMinLength and isMaxLength", () => { - const schema = S.Struct({ - value: S.String.pipe(S.check(S.isMinLength(3)), S.check(S.isMaxLength(50))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.minLength).toBe(3) - expect(meta.value?.maxLength).toBe(50) - }) - - it("Email format detection", () => { - const schema = S.Struct({ - email: S.Email - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.email?.format).toBe("email") - expect(meta.email?.type).toBe("string") - }) -}) - -// ============================================================================ -// 7. TaggedStruct API -// ============================================================================ - -describe("TaggedStruct API", () => { - it("TaggedStruct creates struct with _tag literal", () => { - const ts = S.TaggedStruct("A", { a: S.String }) - expect(ts.fields._tag).toBeDefined() - expect(ts.fields.a).toBeDefined() - - const tagProp = ts.ast.propertySignatures.find((p: any) => p.name === "_tag") - expect(S.AST.isLiteral(tagProp!.type)).toBe(true) - expect((tagProp!.type as any).literal).toBe("A") - }) - - it("Union([TaggedStruct, TaggedStruct]) generates field metadata", () => { - const taggedSchema = S.Struct({ - union: S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255 }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - }) - - const { meta } = generateMetaFromSchema(taggedSchema) - - expect(meta["union.a"]?.type).toBe("string") - expect(meta["union.b"]?.type).toBe("number") - expect(meta["union._tag"]).toBeDefined() - }) - - it("NullOr(Union([TaggedStruct])) works for nullable discriminated unions", () => { - const schema = S.Struct({ - union: S.NullOr( - S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255, common: S.String }), - S.TaggedStruct("B", { b: S.Finite, common: S.String }) - ]) - ) - }) - - const { meta } = generateMetaFromSchema(schema) - expect(meta["union._tag"]?.required).toBe(false) - expect(meta["union.a"]?.required).toBe(true) - expect(meta["union.b"]?.required).toBe(true) - }) - - it("TaggedStruct defaults work with withConstructorDefault", () => { - const schema = S.Union([ - S.TaggedStruct("A", { - a: S.String.pipe(S.withConstructorDefault(Effect.succeed("defaultA"))) - }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const defaults = defaultsValueFromSchema(schema) - expect(defaults.a).toBe("defaultA") - }) - - it("TaggedStruct decoding works correctly", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const decoded = S.decodeUnknownSync(schema)({ _tag: "A", a: "hello" }) - expect(decoded).toEqual({ _tag: "A", a: "hello" }) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", b: 42 }) - expect(decoded2).toEqual({ _tag: "B", b: 42 }) - }) -}) - -// ============================================================================ -// 8. Array metadata -// ============================================================================ - -describe("array metadata", () => { - it("Array of primitives generates multiple type", () => { - const schema = S.Struct({ - tags: S.Array(S.String) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.tags?.type).toBe("multiple") - }) - - it("Array of structs generates nested field metadata", () => { - const schema = S.Struct({ - items: S.Array(S.Struct({ - name: S.NonEmptyString255, - value: S.Finite - })) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta["items.name"]).toBeDefined() - expect(meta["items.name"]?.type).toBe("string") - expect(meta["items.value"]?.type).toBe("number") - }) -}) - -// ============================================================================ -// 9. withDecodingDefault form integration -// ============================================================================ - -describe("withDecodingDefault form integration", () => { - it("withDecodingDefault defaults flow into form values", async () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("John"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(25))), - active: S.Boolean - }) - - const values = await mountAndGetDefaults(schema) - expect(values.name).toBe("John") - expect(values.age).toBe(25) - expect(values.active).toBe(false) - }) - - it("withDecodingDefault mixed with withConstructorDefault", async () => { - const schema = S.Struct({ - fromDecoding: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("decoding"))), - fromConstructor: S.String.pipe(S.withConstructorDefault(Effect.succeed("constructor"))), - plain: S.Boolean - }) - - const values = await mountAndGetDefaults(schema) - expect(values.fromDecoding).toBe("decoding") - expect(values.fromConstructor).toBe("constructor") - expect(values.plain).toBe(false) - }) - - it("tanstack defaultValues override withDecodingDefault", async () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("fromSchema"))), - other: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(99))) - }) - - const values = await mountAndGetDefaults(schema, { - defaultValues: { name: "fromTanstack" } - }) - expect(values.name).toBe("fromTanstack") - expect(values.other).toBe(99) - }) -}) - -// ============================================================================ -// 10. Regression guards -// ============================================================================ - -describe("regression guards", () => { - it("isBetween extracts min and max metadata", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isBetween({ minimum: 5, maximum: 15 }))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.minimum).toBe(5) - expect(meta.value?.maximum).toBe(15) - }) - - it("withDecodingDefault extracted by defaultsValueFromSchema", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("myDefault"))) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.name).toBe("myDefault") - }) -}) diff --git a/packages/vue-components/__tests__/setup.ts b/packages/vue-components/__tests__/setup.ts deleted file mode 100644 index 45a27c16d..000000000 --- a/packages/vue-components/__tests__/setup.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { config } from "@vue/test-utils" -import { useIntlKey } from "../src/utils" - -const mockUseIntl = () => - ({ - trans: (k: string) => `${k}_translated`, - formatMessage: ( - descriptor: { id?: string; defaultMessage?: string }, - _values?: Record - ) => `${descriptor.id ?? descriptor.defaultMessage ?? ""}_formatted` - }) as any - -config.global.provide = { - ...(config.global.provide ?? {}), - [useIntlKey]: mockUseIntl -} diff --git a/packages/vue-components/__tests__/test-array-meta.test.ts b/packages/vue-components/__tests__/test-array-meta.test.ts deleted file mode 100644 index ec3566229..000000000 --- a/packages/vue-components/__tests__/test-array-meta.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -const schema = S.Struct({ - a: S.Array(S.Struct({ - b: S.String, - c: S.Array(S.Struct({ - d: S.String - })), - e: S.Array(S.Finite) - })), - aMultiple: S.Array(S.String), - aNullable: S.NullOr(S.Struct({ - a: S.String - })), - foo: S - .NullOr( - S.Struct({ - bar: S.UndefinedOr(S.Array(S.Struct({ - baz: S.String, - items: S.NonEmptyArray(S.Struct({ - baz: S.String, - value: S.Finite - })) - }))) - }) - ) -}) - -describe("test-array-meta", () => { - it("should generate metadata for arrays", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - console.log("Keys:", Object.keys(result.meta)) - - expect(result.meta).toHaveProperty("a.b") - expect(result.meta["a.b"].type).toBe("string") - expect(result.meta).toHaveProperty("a.c.d") - expect(result.meta["a.c.d"].type).toBe("string") - expect(result.meta).toHaveProperty("a.e") - expect(result.meta["a.e"].type).toBe("multiple") - expect(result.meta).toHaveProperty("aMultiple") - expect(result.meta["aMultiple"]?.type).toBe("multiple") - expect(result.meta).toHaveProperty("aNullable.a") - expect(result.meta["aNullable.a"]?.type).toBe("string") - expect(result.meta).not.toHaveProperty("aNullable") - expect(result.meta).toHaveProperty("foo.bar.items.baz") - expect(result.meta["foo.bar.items.baz"]?.type).toBe("string") - expect(result.meta).toHaveProperty("foo.bar.items.value") - expect(result.meta["foo.bar.items.value"]?.type).toBe("number") - expect(result.meta).toHaveProperty("foo.bar.baz") - expect(result.meta["foo.bar.baz"]?.type).toBe("string") - }) -}) diff --git a/packages/vue-components/__tests__/test-nullor-duplicate.test.ts b/packages/vue-components/__tests__/test-nullor-duplicate.test.ts deleted file mode 100644 index 9a39284e9..000000000 --- a/packages/vue-components/__tests__/test-nullor-duplicate.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -// Test schema from Array.vue story -const schema = S.Struct({ - height: S - .NullOr( - S.Struct({ - left: S.String, - right: S.String, - internal: S.String - }) - ), - width: S.Struct({ - left: S.String, - right: S.String - }) -}) - -describe("test-nullor-duplicate", () => { - it("should not create duplicate height keys for NullOr struct", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - console.log("Keys:", Object.keys(result.meta)) - - // Should have nested fields for height - expect(result.meta).toHaveProperty("height.left") - expect(result.meta).toHaveProperty("height.right") - expect(result.meta).toHaveProperty("height.internal") - - // Should have nested fields for width - expect(result.meta).toHaveProperty("width.left") - expect(result.meta).toHaveProperty("width.right") - - // Should NOT have a parent "height" key (this was the duplicate issue) - expect(result.meta).not.toHaveProperty("height") - - // Regular structs also don't get parent keys, only their nested fields - expect(result.meta).not.toHaveProperty("width") - }) -}) diff --git a/packages/vue-components/__tests__/test-union-meta.test.ts b/packages/vue-components/__tests__/test-union-meta.test.ts deleted file mode 100644 index 5e11fe6cc..000000000 --- a/packages/vue-components/__tests__/test-union-meta.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -const schema = S.Struct({ - mine: S.Struct({ their: S.Union([S.String, S.Struct({ yours: S.String })]) }) -}) - -describe("test-union-meta", () => { - it("should generate metadata for union", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - - expect(result.meta).toHaveProperty("mine.their") - expect(result.meta).toHaveProperty("mine.their.yours") - }) -}) diff --git a/packages/vue-components/__tests__/tsconfig.json b/packages/vue-components/__tests__/tsconfig.json deleted file mode 100644 index be9137dfa..000000000 --- a/packages/vue-components/__tests__/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "types": [ - "vite/client", - "vitest/globals" - ] - } -} diff --git a/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md b/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md deleted file mode 100644 index db53cdf8e..000000000 --- a/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md +++ /dev/null @@ -1,2655 +0,0 @@ -# OmegaForm Refactor Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Decompose `OmegaFormStuff.ts` (1422 lines) and `useOmegaForm.ts` (1022 lines) into focused modules, slim the schema-meta extraction, centralize validation onto a single localized form-level schema, and remove dead workarounds. Public API unchanged. - -**Architecture:** Five sequential phases. Phase 0 writes characterization tests against current behavior. Phase 1 splits files by concern (no behavior change). Phase 2 slims `createMeta` and adopts the v4 `format` annotation for email detection. Phase 3 centralizes validation and deletes the per-field schema and the errorMap-clearing workaround. Phase 4 is independent cleanup. Each task makes one self-contained, atomically committable change. - -**Tech Stack:** - -- TypeScript, Vue 3 (` -``` - -And replace the template's `` with: - -```vue - -``` - -Note the deletions: `:key="fieldKey"`, `:validators="{ ...validators, onSubmit: schema }"`, the `schema` computed, the `composeStandardSchemas` helper, the `hasIssues` helper, the `useIntl` call, the `fieldKey` computed. - -- [ ] **Step 2: Run tests + smoke** - -Run: `pnpm test:run` -Expected: green. - -Manually open the `IntegerValidationGerman` story and verify German validation messages still appear. They should — they now come from the form-level localized schema, not the per-field one. If they don't appear, debug Task 3.1. - -- [ ] **Step 3: Commit** - -```bash -git add src/components/OmegaForm/OmegaInput.vue -git commit -m "refactor(omegaform): delete per-field schema and fieldKey re-mount" -``` - ---- - -### Task 3.5: Delete `originalSchema` plumbing and `generateInputStandardSchemaFromFieldMeta` - -**Files:** - -- Modify: `src/components/OmegaForm/meta/types.ts` -- Modify: `src/components/OmegaForm/meta/createMeta.ts` -- Modify: `src/components/OmegaForm/validation/localized.ts` -- Modify: `src/components/OmegaForm/index.ts` -- Delete: `__tests__/OmegaForm/DiscriminatorFieldValidation.test.ts` - -- [ ] **Step 1: Delete the type field** - -In `meta/types.ts`, remove the `originalSchema?: StandardSchemaV1` field from `BaseFieldMeta`. - -- [ ] **Step 2: Delete the attachment plumbing** - -In `meta/createMeta.ts`, delete: - -- `attachOriginalSchemas` -- `toFieldStandardSchema` -- `fieldAstByPath` collection variable everywhere it's plumbed through `createMeta` / `walk` -- All call sites of `attachOriginalSchemas(...)` - -- [ ] **Step 3: Delete the per-field schema generator** - -In `validation/localized.ts`, delete `generateInputStandardSchemaFromFieldMeta` (~150 lines). - -- [ ] **Step 4: Update `index.ts`** - -Remove the export of `generateInputStandardSchemaFromFieldMeta`. - -- [ ] **Step 5: Delete `DiscriminatorFieldValidation.test.ts`** - -This test directly references `originalSchema` / `originalCodec` / `generateInputStandardSchemaFromFieldMeta` via `Reflect.get`. With those gone, the test is testing a deleted API. The form-level schema already validates discriminator values; that's covered by `TaggedUnionRoot.test.ts`'s `_tag` assertions plus the runtime story. - -```bash -git rm __tests__/OmegaForm/DiscriminatorFieldValidation.test.ts -``` - -- [ ] **Step 6: Run tests + tsc + lint** - -Run: `pnpm test:run && pnpm check && pnpm lint` -Expected: green. - -- [ ] **Step 7: Commit** - -```bash -git add -A -git commit -m "refactor(omegaform): delete originalSchema plumbing and per-field schema generator" -``` - ---- - -### Task 3.6: Phase 3 green-bar checkpoint - -- [ ] **Step 1: Full test suite** - -Run: `pnpm test:run && pnpm check && pnpm lint` -Expected: green. - -- [ ] **Step 2: Smoke check all five stories** - -Especially `IntegerValidationGerman` (proves form-level localization works) and `RootLevelTaggedUnion` (proves union meta still drives the right-required-flag-per-tag behavior). - -- [ ] **Step 3: Tag** - -```bash -git tag omegaform-refactor-phase3-green -``` - ---- - -## Phase 4 — Cleanup (each task is an independent commit) - -These tasks can be done in any order or interleaved with bug-fix work; they don't have inter-dependencies. - -### Task 4.1: Translate the German confirm dialog - -**Files:** - -- Modify: `src/components/OmegaForm/blockDialog.ts` -- Modify: `stories/OmegaForm.stories.ts` (add the translation key to the German mock) - -- [ ] **Step 1: Modify `blockDialog.ts`** - -Find the line: - -```ts -if (!confirm("Es sind ungespeicherte Änderungen vorhanden. Wirklich schließen?")) { -``` - -Replace with: - -```ts -const message = formatMessage({ - id: "form.unsaved_changes_confirm", - defaultMessage: "There are unsaved changes. Are you sure you want to close?" -}) -if (!confirm(message)) { -``` - -You'll need to inject `useIntl` at the top of the function: - -```ts -import { useIntl } from "../../utils" -``` - -(Or accept `formatMessage` as an optional dependency injected by the caller — match the existing pattern in `errors.ts`.) - -- [ ] **Step 2: Add the German translation in storybook decorator** - -In `stories/OmegaForm.stories.ts`, add to `germanTranslations`: - -```ts -"form.unsaved_changes_confirm": "Es sind ungespeicherte Änderungen vorhanden. Wirklich schließen?" -``` - -- [ ] **Step 3: Smoke-check** - -Open the `DialogBlockingExamples` story (with German locale toggled on) and trigger the confirm dialog by editing a field then attempting to close. Confirm the German text appears. - -- [ ] **Step 4: Commit** - -```bash -git add src/components/OmegaForm/blockDialog.ts stories/OmegaForm.stories.ts -git commit -m "i18n(omegaform): translate unsaved changes confirm dialog" -``` - ---- - -### Task 4.2: Drop deprecated string-typed fields _(conditional on consumer audit)_ - -**Files:** - -- Modify: `src/components/OmegaForm/types.ts` (formerly `OmegaFormStuff.ts`) -- Modify: `src/components/OmegaForm/useOmegaForm.ts` - -- [ ] **Step 1: Audit consumer apps** - -Run from the repo root: - -```bash -grep -rn 'overrideDefaultValues\|"deprecated:' apps/ 2>/dev/null -grep -rn 'defaultFromSchema' apps/ 2>/dev/null -grep -rn 'OmegaArrayProps.*items' apps/ 2>/dev/null -``` - -If any matches: skip this task, file an issue, keep the deprecated field one more cycle. - -If no matches: proceed. - -- [ ] **Step 2: Remove the deprecated string-typed fields** - -In `OmegaConfig` (in `useOmegaForm.ts` or wherever it landed): - -- Remove `overrideDefaultValues?: "deprecated: use defaultValuesPriority"` -- Remove `defaultFromSchema?: "deprecated: use defaultValuesPriority"` - -In `OmegaArrayProps`: - -- Remove `items?: "please use \`defaultItems\` instead"` - -- [ ] **Step 3: Run tests + tsc** - -Run: `pnpm test:run && pnpm check` -Expected: green. - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "refactor(omegaform): drop deprecated string-typed config fields" -``` - ---- - -### Task 4.3: Drop legacy `field` slot in OmegaArray.vue _(conditional)_ - -**Files:** - -- Modify: `src/components/OmegaForm/OmegaArray.vue` - -- [ ] **Step 1: Audit consumer apps** - -```bash -grep -rn '

: P - -export const nominal: >() => Constructor = < - A extends B.Brand ->(): Constructor< - A -> => B.nominal() as any diff --git a/packages/effect-app/src/Schema/email.ts b/packages/effect-app/src/Schema/email.ts deleted file mode 100644 index 46cc36d28..000000000 --- a/packages/effect-app/src/Schema/email.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { Refinement } from "effect-app/Function" -import { isValidEmail } from "effect-app/validation" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import type { B } from "./schema.ts" -import type { NonEmptyStringBrand } from "./strings.ts" - -export interface EmailBrand extends Simplify> {} - -export type Email = string & EmailBrand - -export const Email = S - .String - .pipe( - S.annotate({ - title: "Email", - description: "an email according to RFC 5322", - format: "email" - }), - S.check(S.isMinLength(3), /* a@b */ S.isMaxLength(998)), - S.refine(isValidEmail as Refinement, { - identifier: "Email", - description: "an email according to RFC 5322", - // JSON Schema minLength counts code points; isMinLength(3) is UTF-16 units (ceil(3/2)=2). - jsonSchema: { format: "email", minLength: 2, maxLength: 998 }, - // Native Arbitrary cannot invert `isValidEmail`; without a constraint, rejection sampling - // discards (almost) every random string. - arbitraryConstraint: { - patterns: [{ source: "^[a-z]{3,10}@[a-z]{3,10}\\.(?:com|net|org|de)$", flags: "" }] - } - }) - ) diff --git a/packages/effect-app/src/Schema/ext.ts b/packages/effect-app/src/Schema/ext.ts deleted file mode 100644 index e19242ee8..000000000 --- a/packages/effect-app/src/Schema/ext.ts +++ /dev/null @@ -1,688 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/** - * # `withConstructorDefault` policy - * - * The `withConstructorDefault` properties exported throughout this module - * (and from `numbers.ts`, `moreStrings.ts`, `ids.ts`) attach a default value - * that is **only** applied during construction — i.e. when the field is - * omitted from the input to a Schema constructor / `.make(...)` call. - * - * They are **NOT** applied during `decode` (JSON, database rows, RPC payloads, - * etc.). Decoding a payload with a missing field will still fail with a parse - * error, exactly as if the default were not present. - * - * Concretely this means `withConstructorDefault` MUST NOT be relied on as a - * just-in-time migration mechanism for database fields. If a stored record is - * missing a newly added field, the constructor default will not fill it in on - * read — decoding will fail. - * - * ## Don't reach for `withDecodingDefault*` either - * - * The sibling `withDecodingDefaultType` (and `withDecodingDefault`) extensions - * exist, but they are discouraged for migrating persisted data. A missing - * field in a stored record is just as likely to be data corruption as it is - * an old-shape document; silently substituting a default hides the problem - * and can poison downstream aggregates. - * - * Prefer an **explicit, preferably versioned** migration of database data - * (a schema-version field, a one-shot backfill, or a transform on read that - * is gated on an explicit version marker) over shoving missing fields under - * the rug with a decode-time default. - */ -import * as Config from "effect/Config" -import * as Effect from "effect/Effect" -import { pipe } from "effect/Function" -import * as Function from "effect/Function" -import * as Option from "effect/Option" -import * as Order from "effect/Order" -import * as S from "effect/Schema" -import * as SchemaIssue from "effect/SchemaIssue" -import * as SchemaTransformation from "effect/SchemaTransformation" -import * as Struct from "effect/Struct" -import { type NonEmptyReadonlyArray } from "../Array.ts" -import * as Context from "../Context.ts" -import * as SchemaAST from "../SchemaAST.ts" -import { extendM, typedKeysOf } from "../utils.ts" -import { type AST } from "./schema.ts" - -type ProvidedCodec = S.Codec< - Self["Type"], - Self["Encoded"], - Exclude, - Exclude -> - -const concurrencySetting = Effect.runSync( - Config - .Literal("unbounded", "SCHEMA_CONCURRENCY") - .pipe(Config.orElse(() => Config.Number("SCHEMA_CONCURRENCY")), Config.option) -) - -export const DefaultParseOptions: SchemaAST.ParseOptions = { - concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const) -} - -/** - * Parse-options annotation used on schema constructors for decode paths where callers - * cannot currently pass parse options (notably some RPC / HttpApi integration paths). - * - * Keep this annotation in place so those framework-managed decodes still run with - * unbounded concurrency by default. - */ -export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const - -type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any - -export const withDefaultParseOptions = ( - decode: Decode, - defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions -): Decode => - ((schema: any) => { - const run = decode(schema) - return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options }) - }) as Decode - -const DateString = S.String.annotate({ - identifier: "DateOrInvalid", - description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)", - format: "date-time" -}) - -/** - * Schema type for {@link DateFromString}. - * - * @category Schemas - * @since 4.0.0 - */ -export interface DateFromString extends S.decodeTo {} - -/** - * A transformation schema that parses an ISO 8601 string into a `Date`. - * - * Decoding: - * - A `string` is decoded as a `Date`. - * - * Encoding: - * - A `Date` is encoded as a `string`. - * - * Use this when the Encoded form must be JSON (`string`). Domain models should - * prefer {@link Date}, whose Encoded form is `Date`; JSON stores convert via - * `Schema.toCodecJson`. - * - * @since 4.0.0 - */ -export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString)) - -const dateHelpers = (s: S.Date) => ({ - /** - * Construction-only default `new Date()`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — cannot be - * used to JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))), - /** - * Decode-time default `new Date()`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; silently - * substituting `new Date()` hides the problem. Prefer an explicit, - * preferably versioned migration over a decode-time fallback. See - * file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date()))) -}) - -/** Like the default Schema `Date` (Encoded is `Date`) with default helpers. */ -export const Date = extendM(S.Date, dateHelpers) - -/** - * Alias of {@link Date}. Core `Schema.Date` already rejects invalid Dates. - * - * @deprecated Use {@link Date}. - */ -export const DateValid = Date - -/** Like the default Schema `Boolean` but with default helpers. */ -export const Boolean = Object.assign(S.Boolean, { - /** - * Construction-only default `false`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — cannot be - * used to JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))), - /** - * Decode-time default `false`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; silently - * substituting `false` hides the problem. Prefer an explicit, preferably - * versioned migration over a decode-time fallback. See file-level note. - */ - withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false))) -}) - -/** - * You probably want to use `Finite` instead of this. Like the default Schema - * `Number` but with default helpers. - */ -export const Number = Object.assign(S.Number, { - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))), - /** - * Decode-time default `0`. **Discouraged for persisted data:** a missing - * field may be data corruption, not an old-shape document; silently - * substituting `0` hides the problem. Prefer an explicit, preferably - * versioned migration over a decode-time fallback. See file-level note. - */ - withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0))) -}) - -// Generation-only: native Arbitrary otherwise samples up to ±Number.MAX_VALUE, so -// derived totals overflow to Infinity. Validation and JSON Schema are unaffected. -const realisticFiniteSamples = S.makeFilter(() => undefined, { - arbitraryConstraint: { order: Order.Number, minimum: -1_000_000, maximum: 1_000_000 } -}) -const FiniteSchema = S.Finite.pipe(S.check(realisticFiniteSamples)) - -/** Like the default Schema `Finite` but with default helpers. */ -export const Finite = Object.assign(FiniteSchema, { - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: FiniteSchema.pipe(S.withConstructorDefault(Effect.succeed(0))), - /** - * Decode-time default `0`. **Discouraged for persisted data:** a missing - * field may be data corruption, not an old-shape document; silently - * substituting `0` hides the problem. Prefer an explicit, preferably - * versioned migration over a decode-time fallback. See file-level note. - */ - withDecodingDefaultType: FiniteSchema.pipe(S.withDecodingDefaultType(Effect.succeed(0))) -}) - -/** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */ -export const Literals = >(literals: Literals) => - pipe( - S.Literals(literals), - (s) => - Object.assign(s, { - /** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */ - changeDefault: (a: A) => { - return Object.assign(S.Literals(literals), { - Default: a, - /** - * Construction-only default. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level - * note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))), - /** - * Decode-time default. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape - * document; silently substituting hides the problem. Prefer an - * explicit, preferably versioned migration over a decode-time - * fallback. See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a))) - }) // todo: copy annotations from original? - }, - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- load-bearing: Object.assign widens the field type without it, breaking `expectTypeOf(l.Default).toEqualTypeOf<"a">()` in tests - Default: literals[0] as Literals[0], - /** - * Construction-only default `literals[0]`. Applied only when the - * field is omitted from `.make(...)` input. NOT applied during - * decode — cannot be used to JIT-migrate database fields. See - * file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))), - /** - * Decode-time default `literals[0]`. **Discouraged for persisted - * data:** a missing field may be data corruption, not an old-shape - * document; silently substituting hides the problem. Prefer an - * explicit, preferably versioned migration over a decode-time - * fallback. See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0]))) - }) - ) - -/** Like the default Schema `Array` but with default helpers. */ -export function Array(value: ValueSchema) { - return pipe( - S.Array(value).annotate(concurrencyUnbounded), - (s) => - Object.assign(s, { - /** - * Construction-only default `[]`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level - * note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))), - /** - * Decode-time default `[]`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; - * silently substituting `[]` hides the problem. Prefer an explicit, - * preferably versioned migration over a decode-time fallback. See - * file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => []))) - }) - ) -} - -/** - * An annotated `S.Array` of unique items that decodes to a `ReadonlySet`. - */ -export const ReadonlySetFromArray = (value: ValueSchema) => { - const from = S - .Array(value) - .annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" }) - const to = S.instanceOf(Set) as S.instanceOf> - const schema = from.pipe( - S.decodeTo( - to, - SchemaTransformation.transform({ - decode: (arr) => new Set(arr) as ReadonlySet, - encode: (set) => [...set] - }) - ) - ) - return schema -} - -/** - * An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`. - */ -export const ReadonlyMapFromArray = (pair: { - readonly key: KeySchema - readonly value: ValueSchema -}) => { - const from = S - .Array(S.Tuple([pair.key, pair.value])) - .annotate({ - ...concurrencyUnbounded, - expected: "an array of key-value tuples that will be decoded as a ReadonlyMap" - }) - const to = S.instanceOf(Map) as S.instanceOf< - ReadonlyMap - > - const schema = from.pipe( - S.decodeTo( - to, - SchemaTransformation.transform({ - decode: ( - arr - ) => new Map(arr) as ReadonlyMap, - encode: ( - map - ) => [...map.entries()] as any // fu - }) - ) - ) - return schema -} - -/** Like the default Schema `ReadonlySet` (Encoded is `Set`) with default helpers. */ -export const ReadonlySet = (value: ValueSchema) => - pipe( - S.ReadonlySet(value), - (s) => - Object.assign(s, { - /** - * Construction-only default `new Set()`. Applied only when the field - * is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level - * note. - */ - withConstructorDefault: s.pipe( - S.withConstructorDefault(Effect.sync(() => new Set())) - ), - /** - * Decode-time default `new Set()`. **Discouraged for persisted - * data:** a missing field may be data corruption, not an old-shape - * document; silently substituting an empty set hides the problem. - * Prefer an explicit, preferably versioned migration over a - * decode-time fallback. See file-level note. - */ - withDecodingDefaultType: s.pipe( - S.withDecodingDefaultType(Effect.sync(() => new Set())) - ) - }) - ) - -/** Like the default Schema `ReadonlyMap` (Encoded is `Map`) with default helpers. */ -export const ReadonlyMap = (pair: { - readonly key: KeySchema - readonly value: ValueSchema -}) => - pipe( - S.ReadonlyMap(pair.key, pair.value), - (s) => - Object.assign(s, { - /** - * Construction-only default `new Map()`. Applied only when the field - * is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level - * note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))), - /** - * Decode-time default `new Map()`. **Discouraged for persisted - * data:** a missing field may be data corruption, not an old-shape - * document; silently substituting an empty map hides the problem. - * Prefer an explicit, preferably versioned migration over a - * decode-time fallback. See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map()))) - }) - ) - -/** Like the default Schema `NullOr` but with default helpers. */ -export const NullOr = (self: Schema) => - pipe( - S.NullOr(self), - (s) => - Object.assign(s, { - /** - * Construction-only default `null`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level - * note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))), - /** - * Decode-time default `null`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; - * silently substituting `null` hides the problem. Prefer an - * explicit, preferably versioned migration over a decode-time - * fallback. See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null))) - }) - ) - -/** - * Attach a `withConstructorDefault` of `new Date()` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultDate = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))) - -/** - * Attach a `withConstructorDefault` of `false` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultBool = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.succeed(false))) - -/** - * Attach a `withConstructorDefault` of `null` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultNullable = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.succeed(null))) - -/** - * Attach a `withConstructorDefault` of `[]` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultArray = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.sync(() => []))) - -/** - * Attach a `withConstructorDefault` of `new Map()` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultMap = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))) - -/** - * Attach a `withConstructorDefault` of `new Set()` to any schema. - * - * **Construction-only.** Applied only when the field is omitted from - * `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ -export const defaultSet = (schema: Schema) => - schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set()))) - -export const withDefaultMake = (s: Self) => { - const a = Object.assign(S.decodeSync(s as any) as WithDefaults, s) - Object.setPrototypeOf(a, s) - return a - - // return s as Self & WithDefaults -} - -export type WithDefaults = ( - i: Self["Encoded"], - options?: SchemaAST.ParseOptions -) => Self["Type"] - -// type GetKeys = U extends Record ? K : never -// type UnionToIntersection2 = { -// readonly [K in GetKeys]: U extends Record ? T : never -// } - -// export type Test