diff --git a/.changeset/update-effect-4.0.2.md b/.changeset/update-effect-4.0.2.md new file mode 100644 index 000000000..844a1c4ba --- /dev/null +++ b/.changeset/update-effect-4.0.2.md @@ -0,0 +1,6 @@ +--- +"effect-app": patch +"@effect-app/cli": patch +--- + +Upgrade Effect packages to npm `4.0.2`. 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 diff --git a/repos/effect/.agents/skills/changesets/authoring.md b/repos/effect/.agents/skills/changesets/authoring.md index e1a1c22fe..40d4ef924 100644 --- a/repos/effect/.agents/skills/changesets/authoring.md +++ b/repos/effect/.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/jsdocs/declarations.md b/repos/effect/.agents/skills/jsdocs/declarations.md index 9795b4c9a..770d4ef07 100644 --- a/repos/effect/.agents/skills/jsdocs/declarations.md +++ b/repos/effect/.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/.github/workflows/check.yml b/repos/effect/.github/workflows/check.yml index 5bbcfe849..b52e80d53 100644 --- a/repos/effect/.github/workflows/check.yml +++ b/repos/effect/.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/repos/effect/.github/workflows/effect-bot.yml b/repos/effect/.github/workflows/effect-bot.yml new file mode 100644 index 000000000..3206fac40 --- /dev/null +++ b/repos/effect/.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/repos/effect/MIGRATION.md b/repos/effect/MIGRATION.md index 98b2c635c..4df8d9eaf 100644 --- a/repos/effect/MIGRATION.md +++ b/repos/effect/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/repos/effect/ai-docs/package.json b/repos/effect/ai-docs/package.json index 60c4a4afc..1c21dce10 100644 --- a/repos/effect/ai-docs/package.json +++ b/repos/effect/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/tsconfig.json b/repos/effect/ai-docs/tsconfig.json index e5be14e9b..8aad2d936 100644 --- a/repos/effect/ai-docs/tsconfig.json +++ b/repos/effect/ai-docs/tsconfig.json @@ -16,7 +16,8 @@ "namespaceImportPackages": [], "diagnosticSeverity": { "floatingEffect": "off", - "preferSchemaOverJson": "off" + "preferSchemaOverJson": "off", + "unstableApiUsage": "off" } } ] diff --git a/repos/effect/jsdocs.config.json b/repos/effect/jsdocs.config.json index 6811e35b4..4be2788fb 100644 --- a/repos/effect/jsdocs.config.json +++ b/repos/effect/jsdocs.config.json @@ -8,7 +8,6 @@ "**/node_modules/**", "packages/**/ai-docs/**", "packages/tools/**", - "packages/**/src/index.ts", "packages/effect/src/StandardSchema.ts", "packages/**/src/*Generated.ts", "packages/**/src/internal/**", diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml index 52fb66673..0f1a0c05b 100644 --- a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml +++ b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml @@ -2,8 +2,14 @@ replacement: "OpenAiTelemetry.addGenAIAnnotations" note: "Still exported in v4; update imports and adapt to the revised v4 service and schema types." "@effect/ai-openai/OpenAiTelemetry#AllAttributes": - replacement: "OpenAiTelemetry.AllAttributes" - note: "Still exported in v4; update imports and adapt to the revised v4 service and schema types." + replacement: "none" + note: "Removed. Use OpenAiTelemetry.OpenAiTelemetryAttributeOptions for the grouped options accepted by OpenAiTelemetry.addGenAIAnnotations, or OpenAiTelemetry.OpenAiTelemetryAttributes for the prefixed span attribute keys." "@effect/ai-openai/OpenAiTelemetry#OpenAiTelemetryAttributeOptions": replacement: "OpenAiTelemetry.OpenAiTelemetryAttributeOptions" note: "Still exported in v4; update imports and adapt to the revised v4 service and schema types." +"@effect/ai-openai/OpenAiTelemetry#RequestAttributes": + replacement: "OpenAiTelemetry.RequestAttributes" + note: "Attributes are written under openai.request.* instead of gen_ai.openai.request.*. responseFormat was removed; LanguageModel records gen_ai.output.type for every provider." +"@effect/ai-openai/OpenAiTelemetry#WellKnownResponseFormat": + replacement: "Telemetry.WellKnownOutputType" + note: "The response format is no longer a telemetry option. LanguageModel records gen_ai.output.type as text or json from the requested response format." diff --git a/repos/effect/migration/annotations/effect__ai__Telemetry.yaml b/repos/effect/migration/annotations/effect__ai__Telemetry.yaml new file mode 100644 index 000000000..f815ee141 --- /dev/null +++ b/repos/effect/migration/annotations/effect__ai__Telemetry.yaml @@ -0,0 +1,15 @@ +"@effect/ai/Telemetry#AllAttributes": + replacement: "none" + note: "Removed. Use Telemetry.GenAITelemetryAttributeOptions for the grouped options accepted by Telemetry.addGenAIAnnotations, or Telemetry.GenAITelemetryAttributes for the prefixed gen_ai.* span attribute keys." +"@effect/ai/Telemetry#BaseAttributes": + replacement: "Telemetry.ProviderAttributes" + note: "gen_ai.system was replaced by gen_ai.provider.name. Pass provider: { name } instead of system; legacy system values are not mapped." +"@effect/ai/Telemetry#GenAITelemetryAttributeOptions": + replacement: "Telemetry.GenAITelemetryAttributeOptions" + note: "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." +"@effect/ai/Telemetry#TokenAttributes": + replacement: "none" + note: "Removed with the token option. gen_ai.token.type is not a span attribute in the current OpenTelemetry GenAI conventions." +"@effect/ai/Telemetry#WellKnownSystem": + replacement: "Telemetry.WellKnownProviderName" + note: "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." diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml b/repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml index d936bcb8c..e26cfe0b8 100644 --- a/repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml +++ b/repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml @@ -1,3 +1,15 @@ "@effect/sql-sqlite-bun/SqliteClient#SqliteClient": replacement: "@effect/sql-sqlite-bun/SqliteClient#SqliteClient" note: "Retained; the service value is now a Context.Service." +"@effect/sql-sqlite-bun/SqliteClient#make": + include: true + replacement: "SqliteClient.make" + note: "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-bun/SqliteClient#layer": + include: true + replacement: "SqliteClient.layer" + note: "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." +"@effect/sql-sqlite-bun/SqliteClient#layerConfig": + include: true + replacement: "SqliteClient.layerConfig" + note: "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." diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml b/repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml index 5bbf9cfbe..974a676ee 100644 --- a/repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml +++ b/repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml @@ -4,3 +4,15 @@ "@effect/sql-sqlite-node/SqliteClient#SqliteClientConfig": replacement: "@effect/sql-sqlite-node/SqliteClient#SqliteClientConfig" note: "Retained; prepareCacheTTL now uses Duration.Input." +"@effect/sql-sqlite-node/SqliteClient#make": + include: true + replacement: "SqliteClient.make" + note: "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-node/SqliteClient#layer": + include: true + replacement: "SqliteClient.layer" + note: "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." +"@effect/sql-sqlite-node/SqliteClient#layerConfig": + include: true + replacement: "SqliteClient.layerConfig" + note: "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." diff --git a/repos/effect/migration/v3-to-v4.md b/repos/effect/migration/v3-to-v4.md index 632f133dc..d10fe28cf 100644 --- a/repos/effect/migration/v3-to-v4.md +++ b/repos/effect/migration/v3-to-v4.md @@ -4,7 +4,7 @@ Base: `origin/v3` (`e730f59d29db5f1e312be6476a95d07bcad45788`) -Head: `HEAD` (`a84d4f65d978bf33aabc38227e477d187ff05c2c`) +Head: `HEAD` (`79b88977582fb715494d08405c73a47a593afced`) This file is generated from the API diff and `migration/annotations/*.yaml`. @@ -4152,10 +4152,14 @@ effect/rpc/Utils (barrel: effect/rpc) ### `@effect/ai-openai/OpenAiTelemetry` -- `OpenAiTelemetry.AllAttributes` -> `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/package.json b/repos/effect/package.json index 9cd72218e..572fb0d81 100644 --- a/repos/effect/package.json +++ b/repos/effect/package.json @@ -30,7 +30,7 @@ "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", + "changeset-version": "changeset version && node scripts/version.mjs", "changeset-publish": "node scripts/set-strip-internal.mjs && pnpm codemod && pnpm build && changeset publish" }, "devDependencies": { @@ -46,7 +46,7 @@ "@effect/doctest": "workspace:^", "@effect/jsdocs": "workspace:^", "@effect/oxc": "workspace:^", - "@effect/tsgo": "^0.46.1", + "@effect/tsgo": "^0.51.0", "@effect/utils": "workspace:^", "@effect/vitest": "workspace:^", "@faker-js/faker": "^10.6.0", @@ -54,30 +54,30 @@ "@rollup/plugin-replace": "^6.0.3", "@rollup/plugin-terser": "^1.0.0", "@types/jscodeshift": "^17.3.0", - "@types/node": "^26.6.3", - "@vitest/coverage-v8": "^5.0.2", - "@vitest/web-worker": "^5.0.2", + "@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.57.4", + "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", - "oxlint": "^1.85.0", + "oxlint": "^1.86.0", "pkg-pr-new": "0.0.88", "playwright": "^1.63.0", - "rollup": "^4.63.5", + "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.1", - "vitest": "^5.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/repos/effect/packages/ai/anthropic/CHANGELOG.md index f209b0986..ccd63f7aa 100644 --- a/repos/effect/packages/ai/anthropic/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/ai/anthropic/package.json index fc63c76f4..eb3b2c36a 100644 --- a/repos/effect/packages/ai/anthropic/package.json +++ b/repos/effect/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/AnthropicConfig.ts b/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts index 8c41f4314..f3f8122ca 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts +++ b/repos/effect/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/repos/effect/packages/ai/anthropic/src/AnthropicError.ts index 08d9abe2b..ad3b68b73 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicError.ts +++ b/repos/effect/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/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts index 251009d5c..3709ddec4 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts +++ b/repos/effect/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/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts index 93fe32612..672deb2b3 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts +++ b/repos/effect/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/index.ts b/repos/effect/packages/ai/anthropic/src/index.ts index 9882f693f..844df9eb1 100644 --- a/repos/effect/packages/ai/anthropic/src/index.ts +++ b/repos/effect/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/test/AnthropicLanguageModel.test.ts b/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts index 50e37c586..5f240511d 100644 --- a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +++ b/repos/effect/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/cloudflare/CHANGELOG.md b/repos/effect/packages/ai/cloudflare/CHANGELOG.md index f57ee39b7..87470785c 100644 --- a/repos/effect/packages/ai/cloudflare/CHANGELOG.md +++ b/repos/effect/packages/ai/cloudflare/CHANGELOG.md @@ -1,5 +1,13 @@ # @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 diff --git a/repos/effect/packages/ai/cloudflare/package.json b/repos/effect/packages/ai/cloudflare/package.json index 56b7826f8..b7e707957 100644 --- a/repos/effect/packages/ai/cloudflare/package.json +++ b/repos/effect/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/index.ts b/repos/effect/packages/ai/cloudflare/src/index.ts index bcabe60b4..7b7411acc 100644 --- a/repos/effect/packages/ai/cloudflare/src/index.ts +++ b/repos/effect/packages/ai/cloudflare/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai-compat/CHANGELOG.md b/repos/effect/packages/ai/openai-compat/CHANGELOG.md index a1238e469..d588c5c9a 100644 --- a/repos/effect/packages/ai/openai-compat/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/ai/openai-compat/package.json index eff372e60..361b19cb8 100644 --- a/repos/effect/packages/ai/openai-compat/package.json +++ b/repos/effect/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/OpenAiConfig.ts b/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts index 91766a29d..8e8099eb6 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts +++ b/repos/effect/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/OpenAiError.ts b/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts index e89e40c41..ad317c58e 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts index 3feeed9c9..b8ba73d30 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts index e2c4af08e..040b26185 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai-compat/src/index.ts index 3b5e53712..3d5650c40 100644 --- a/repos/effect/packages/ai/openai-compat/src/index.ts +++ b/repos/effect/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/test/OpenAiTelemetry.test.ts b/repos/effect/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts new file mode 100644 index 000000000..8ee03ff7a --- /dev/null +++ b/repos/effect/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/CHANGELOG.md b/repos/effect/packages/ai/openai/CHANGELOG.md index 00ead7db7..e7637f174 100644 --- a/repos/effect/packages/ai/openai/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/ai/openai/package.json index 2d4f59645..c041c153b 100644 --- a/repos/effect/packages/ai/openai/package.json +++ b/repos/effect/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/OpenAiConfig.ts b/repos/effect/packages/ai/openai/src/OpenAiConfig.ts index 4257c98db..deecf9722 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiConfig.ts +++ b/repos/effect/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/OpenAiError.ts b/repos/effect/packages/ai/openai/src/OpenAiError.ts index a0568d63b..1f777293e 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiError.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts index 9758884f3..f84ac6377 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai/src/OpenAiSchema.ts index 97a704915..f88b492fd 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiSchema.ts +++ b/repos/effect/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/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts index f5c1e7400..a420da54c 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts +++ b/repos/effect/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/index.ts b/repos/effect/packages/ai/openai/src/index.ts index 77389e360..69d7e26bd 100644 --- a/repos/effect/packages/ai/openai/src/index.ts +++ b/repos/effect/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/test/OpenAiTelemetry.test.ts b/repos/effect/packages/ai/openai/test/OpenAiTelemetry.test.ts new file mode 100644 index 000000000..4cbbc4a49 --- /dev/null +++ b/repos/effect/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/openrouter/CHANGELOG.md b/repos/effect/packages/ai/openrouter/CHANGELOG.md index d6447781b..36088fdc2 100644 --- a/repos/effect/packages/ai/openrouter/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/ai/openrouter/package.json index db373fd35..46b99545b 100644 --- a/repos/effect/packages/ai/openrouter/package.json +++ b/repos/effect/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/OpenRouterConfig.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts index 1939acd31..50db2fde2 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts +++ b/repos/effect/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/OpenRouterError.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts index e9ee93af4..9d9c04a62 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts +++ b/repos/effect/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/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts index bf96042f6..0952a3504 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts +++ b/repos/effect/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/index.ts b/repos/effect/packages/ai/openrouter/src/index.ts index 73678854c..f4b91c02b 100644 --- a/repos/effect/packages/ai/openrouter/src/index.ts +++ b/repos/effect/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/typesafe/CHANGELOG.md b/repos/effect/packages/ai/typesafe/CHANGELOG.md index 018f63668..a7bb9b5da 100644 --- a/repos/effect/packages/ai/typesafe/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/ai/typesafe/package.json index bf45aa03a..28f861cd0 100644 --- a/repos/effect/packages/ai/typesafe/package.json +++ b/repos/effect/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/TypeSafeConfig.ts b/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts index 851216a17..f8efceb0f 100644 --- a/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts +++ b/repos/effect/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/index.ts b/repos/effect/packages/ai/typesafe/src/index.ts index 872b30078..398dfa405 100644 --- a/repos/effect/packages/ai/typesafe/src/index.ts +++ b/repos/effect/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/atom/react/CHANGELOG.md b/repos/effect/packages/atom/react/CHANGELOG.md index a669be84e..d767fc56e 100644 --- a/repos/effect/packages/atom/react/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/atom/react/package.json index ab851da10..33646b6cc 100644 --- a/repos/effect/packages/atom/react/package.json +++ b/repos/effect/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/repos/effect/packages/atom/react/src/Hooks.ts index dc10f753e..87b753565 100644 --- a/repos/effect/packages/atom/react/src/Hooks.ts +++ b/repos/effect/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/repos/effect/packages/atom/react/src/ReactHydration.ts index 62625d715..bbc8d91c5 100644 --- a/repos/effect/packages/atom/react/src/ReactHydration.ts +++ b/repos/effect/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/repos/effect/packages/atom/react/src/RegistryContext.ts index 7ea69b455..3509dccff 100644 --- a/repos/effect/packages/atom/react/src/RegistryContext.ts +++ b/repos/effect/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/repos/effect/packages/atom/react/src/ScopedAtom.ts index 23ab2319f..ea04bc9c8 100644 --- a/repos/effect/packages/atom/react/src/ScopedAtom.ts +++ b/repos/effect/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/repos/effect/packages/atom/react/src/index.ts index 554a0df30..c4350d254 100644 --- a/repos/effect/packages/atom/react/src/index.ts +++ b/repos/effect/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/repos/effect/packages/atom/react/test/index.test.tsx index 6a9e7ecf7..aae21c613 100644 --- a/repos/effect/packages/atom/react/test/index.test.tsx +++ b/repos/effect/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/solid/CHANGELOG.md b/repos/effect/packages/atom/solid/CHANGELOG.md index 268e97f5c..5b0c6d470 100644 --- a/repos/effect/packages/atom/solid/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/atom/solid/package.json index 17fa6dda2..80949332f 100644 --- a/repos/effect/packages/atom/solid/package.json +++ b/repos/effect/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/repos/effect/packages/atom/solid/src/Hooks.ts index a8763e874..7c0877fa6 100644 --- a/repos/effect/packages/atom/solid/src/Hooks.ts +++ b/repos/effect/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/repos/effect/packages/atom/solid/src/RegistryContext.ts index b534f26c3..ab327f76f 100644 --- a/repos/effect/packages/atom/solid/src/RegistryContext.ts +++ b/repos/effect/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/repos/effect/packages/atom/solid/src/index.ts index e8c448255..e6ce741b7 100644 --- a/repos/effect/packages/atom/solid/src/index.ts +++ b/repos/effect/packages/atom/solid/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/vue/CHANGELOG.md b/repos/effect/packages/atom/vue/CHANGELOG.md index 9136e5241..14c477bcf 100644 --- a/repos/effect/packages/atom/vue/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/atom/vue/package.json index 414356bd0..6d7ba713a 100644 --- a/repos/effect/packages/atom/vue/package.json +++ b/repos/effect/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/repos/effect/packages/atom/vue/src/index.ts index ecd8a09c7..39e0a210f 100644 --- a/repos/effect/packages/atom/vue/src/index.ts +++ b/repos/effect/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/effect/CHANGELOG.md b/repos/effect/packages/effect/CHANGELOG.md index a1d0f6816..8a345116a 100644 --- a/repos/effect/packages/effect/CHANGELOG.md +++ b/repos/effect/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/package.json b/repos/effect/packages/effect/package.json index d042e717c..48e82a448 100644 --- a/repos/effect/packages/effect/package.json +++ b/repos/effect/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/src/Array.ts b/repos/effect/packages/effect/src/Array.ts index 65dd5016d..402966e7f 100644 --- a/repos/effect/packages/effect/src/Array.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/BigDecimal.ts index 729fcdae8..ede74868d 100644 --- a/repos/effect/packages/effect/src/BigDecimal.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/BigInt.ts index 18ac6ba68..99ab0b4b4 100644 --- a/repos/effect/packages/effect/src/BigInt.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Boolean.ts index 405e7b35a..dbcc3e5ad 100644 --- a/repos/effect/packages/effect/src/Boolean.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Brand.ts index 1cbba59fd..40b41a5ec 100644 --- a/repos/effect/packages/effect/src/Brand.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ByteSize.ts index ddacee0f7..3300883e4 100644 --- a/repos/effect/packages/effect/src/ByteSize.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Cache.ts index ac406ed5b..6e578eaee 100644 --- a/repos/effect/packages/effect/src/Cache.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Channel.ts index 53357cc35..c6f3a68a1 100644 --- a/repos/effect/packages/effect/src/Channel.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ChannelSchema.ts index b2a84f438..ac4761476 100644 --- a/repos/effect/packages/effect/src/ChannelSchema.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Chunk.ts index 8555f3253..364d98e47 100644 --- a/repos/effect/packages/effect/src/Chunk.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Clock.ts index 5434197be..fa04210c7 100644 --- a/repos/effect/packages/effect/src/Clock.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Combiner.ts index 75ab2d492..d46461421 100644 --- a/repos/effect/packages/effect/src/Combiner.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Config.ts index 5690a6398..ba1819372 100644 --- a/repos/effect/packages/effect/src/Config.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ConfigProvider.ts index 59d4e6f87..4250c4bc2 100644 --- a/repos/effect/packages/effect/src/ConfigProvider.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Console.ts index 83264a74b..f00448c5b 100644 --- a/repos/effect/packages/effect/src/Console.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Context.ts index a3d918ff8..90e6f0d6c 100644 --- a/repos/effect/packages/effect/src/Context.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Cron.ts index 9d921ce56..477667a2a 100644 --- a/repos/effect/packages/effect/src/Cron.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Crypto.ts index fa16326e0..43daf4330 100644 --- a/repos/effect/packages/effect/src/Crypto.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Data.ts index 2228bb0e9..c0e3085a8 100644 --- a/repos/effect/packages/effect/src/Data.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/DateTime.ts index c121861a2..38a7e456a 100644 --- a/repos/effect/packages/effect/src/DateTime.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Deferred.ts index 14e75b964..663d79a2d 100644 --- a/repos/effect/packages/effect/src/Deferred.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Differ.ts index ba636ba64..3480e789a 100644 --- a/repos/effect/packages/effect/src/Differ.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Duration.ts index 2ffd92993..e93556fa9 100644 --- a/repos/effect/packages/effect/src/Duration.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Effect.ts index a4da72c9b..ac90f3f94 100644 --- a/repos/effect/packages/effect/src/Effect.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Effectable.ts index b38518f65..73636f18c 100644 --- a/repos/effect/packages/effect/src/Effectable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Equal.ts index 5ea7eb54e..9b6801ea4 100644 --- a/repos/effect/packages/effect/src/Equal.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Equivalence.ts index e1fe8ce5d..ecfd537fb 100644 --- a/repos/effect/packages/effect/src/Equivalence.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ErrorReporter.ts index 220c141e0..8c5fbcae6 100644 --- a/repos/effect/packages/effect/src/ErrorReporter.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ExecutionPlan.ts index 135385f76..318e17de9 100644 --- a/repos/effect/packages/effect/src/ExecutionPlan.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Exit.ts index 889d81d48..00f9f46c9 100644 --- a/repos/effect/packages/effect/src/Exit.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Fiber.ts index 4eae1625c..672e12429 100644 --- a/repos/effect/packages/effect/src/Fiber.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/FiberHandle.ts index 3f78b4555..015862edf 100644 --- a/repos/effect/packages/effect/src/FiberHandle.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/FiberMap.ts index cdda71b25..2790e50a3 100644 --- a/repos/effect/packages/effect/src/FiberMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/FiberSet.ts index f7cfd58db..d2a285627 100644 --- a/repos/effect/packages/effect/src/FiberSet.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/FileSystem.ts index c737dc17f..e633dd873 100644 --- a/repos/effect/packages/effect/src/FileSystem.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Filter.ts index 631d3e03d..74977b46c 100644 --- a/repos/effect/packages/effect/src/Filter.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Formatter.ts index b184d5c45..3267fd539 100644 --- a/repos/effect/packages/effect/src/Formatter.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Function.ts index edb900285..13e4e7f06 100644 --- a/repos/effect/packages/effect/src/Function.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Graph.ts index f0dbb6730..5fcbd5881 100644 --- a/repos/effect/packages/effect/src/Graph.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/HKT.ts index ae7a5608d..374c37fb7 100644 --- a/repos/effect/packages/effect/src/HKT.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Hash.ts index a66c55a0e..cf018c46a 100644 --- a/repos/effect/packages/effect/src/Hash.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/HashMap.ts index 7a9e80837..60e5be154 100644 --- a/repos/effect/packages/effect/src/HashMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/HashRing.ts index 2288eea34..1711de55d 100644 --- a/repos/effect/packages/effect/src/HashRing.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/HashSet.ts index 2f088d062..df2ed97dc 100644 --- a/repos/effect/packages/effect/src/HashSet.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Inspectable.ts index 47843fffa..ac0ecdad6 100644 --- a/repos/effect/packages/effect/src/Inspectable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Iterable.ts index be5547629..380637585 100644 --- a/repos/effect/packages/effect/src/Iterable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/JsonPatch.ts index 8d0943dd3..929001ade 100644 --- a/repos/effect/packages/effect/src/JsonPatch.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/JsonPointer.ts index 78392b9da..b0aab9d94 100644 --- a/repos/effect/packages/effect/src/JsonPointer.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/JsonSchema.ts index 5cbd69837..125b00f61 100644 --- a/repos/effect/packages/effect/src/JsonSchema.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Latch.ts index 62e73d736..59c2c4787 100644 --- a/repos/effect/packages/effect/src/Latch.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Layer.ts index aed2f1067..982f864e8 100644 --- a/repos/effect/packages/effect/src/Layer.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/LayerMap.ts index 014766e37..4091a4559 100644 --- a/repos/effect/packages/effect/src/LayerMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/LayerRef.ts index 4d18aeb85..f850c5e72 100644 --- a/repos/effect/packages/effect/src/LayerRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/LogLevel.ts index 837e1bd5a..2868cd0c2 100644 --- a/repos/effect/packages/effect/src/LogLevel.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Logger.ts index 2cd9b881a..16edc09af 100644 --- a/repos/effect/packages/effect/src/Logger.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ManagedRuntime.ts index 877935c68..9f11d4412 100644 --- a/repos/effect/packages/effect/src/ManagedRuntime.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Match.ts index 1f3a4aaad..4cd164619 100644 --- a/repos/effect/packages/effect/src/Match.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Metric.ts index c4a2cb31a..9b8e31635 100644 --- a/repos/effect/packages/effect/src/Metric.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/MutableHashMap.ts index 2d788f18b..6ec7e0167 100644 --- a/repos/effect/packages/effect/src/MutableHashMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/MutableHashSet.ts index 654f7c633..9b5f3787b 100644 --- a/repos/effect/packages/effect/src/MutableHashSet.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/MutableList.ts index 061e9d6a8..634d7e8c7 100644 --- a/repos/effect/packages/effect/src/MutableList.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/MutableRef.ts index acd270380..49be9ec68 100644 --- a/repos/effect/packages/effect/src/MutableRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Newtype.ts index 4df67075f..f87bade4e 100644 --- a/repos/effect/packages/effect/src/Newtype.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/NonEmptyIterable.ts index b19aa8c61..8196cd22b 100644 --- a/repos/effect/packages/effect/src/NonEmptyIterable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Number.ts index b7d8f3d61..03fdfa973 100644 --- a/repos/effect/packages/effect/src/Number.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Optic.ts index c21149a6f..107432f29 100644 --- a/repos/effect/packages/effect/src/Optic.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Option.ts index 6feed438b..23895f878 100644 --- a/repos/effect/packages/effect/src/Option.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Order.ts index 211b44bbd..fdf0c2f54 100644 --- a/repos/effect/packages/effect/src/Order.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Ordering.ts index ff93ffca7..892adba14 100644 --- a/repos/effect/packages/effect/src/Ordering.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/PartitionedSemaphore.ts index 0f8a80628..93fdb84f3 100644 --- a/repos/effect/packages/effect/src/PartitionedSemaphore.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Path.ts index 946b18f0a..aca7dfc35 100644 --- a/repos/effect/packages/effect/src/Path.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Pipeable.ts index 6f2697e74..f6be3cdb7 100644 --- a/repos/effect/packages/effect/src/Pipeable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/PlatformError.ts index 4a5f1681a..bc84567bd 100644 --- a/repos/effect/packages/effect/src/PlatformError.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Pool.ts index 2ee2e30cb..123e00371 100644 --- a/repos/effect/packages/effect/src/Pool.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Predicate.ts index f66458aaf..dfab63412 100644 --- a/repos/effect/packages/effect/src/Predicate.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/PrimaryKey.ts index f7ddfa656..77551232c 100644 --- a/repos/effect/packages/effect/src/PrimaryKey.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/PubSub.ts index e8a8e5a83..15ab11e10 100644 --- a/repos/effect/packages/effect/src/PubSub.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Pull.ts index 514b66e02..ca31de1e0 100644 --- a/repos/effect/packages/effect/src/Pull.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Queue.ts index 5a1f93afc..6ea790b82 100644 --- a/repos/effect/packages/effect/src/Queue.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Random.ts index 67b2c5781..e2fc7b61a 100644 --- a/repos/effect/packages/effect/src/Random.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/RcMap.ts index fbb97f76a..d3c5b6cb7 100644 --- a/repos/effect/packages/effect/src/RcMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/RcRef.ts index 8c81fce74..d2e4c3eaa 100644 --- a/repos/effect/packages/effect/src/RcRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Record.ts index 198c3fb5e..74c9af49f 100644 --- a/repos/effect/packages/effect/src/Record.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Redactable.ts index b02eb30f2..e0b234796 100644 --- a/repos/effect/packages/effect/src/Redactable.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Redacted.ts index be67a9e65..e14d731e2 100644 --- a/repos/effect/packages/effect/src/Redacted.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Reducer.ts index 6f1a6e42b..8371399af 100644 --- a/repos/effect/packages/effect/src/Reducer.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Ref.ts index a5c783fca..c1663d44a 100644 --- a/repos/effect/packages/effect/src/Ref.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/References.ts index 5d5e51dd9..95b479181 100644 --- a/repos/effect/packages/effect/src/References.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/RegExp.ts index c1c97ea3e..369d19d8b 100644 --- a/repos/effect/packages/effect/src/RegExp.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Request.ts index 5ccb1032e..4bb5fc8f7 100644 --- a/repos/effect/packages/effect/src/Request.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/RequestResolver.ts index 02239e675..5df4843a8 100644 --- a/repos/effect/packages/effect/src/RequestResolver.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Resource.ts index 7e6d5383e..ef03a2eb9 100644 --- a/repos/effect/packages/effect/src/Resource.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Result.ts index 262a9493a..7b7460925 100644 --- a/repos/effect/packages/effect/src/Result.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Runtime.ts index ab7ad6809..e9769d40c 100644 --- a/repos/effect/packages/effect/src/Runtime.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Schedule.ts index 144692a10..ccf572da2 100644 --- a/repos/effect/packages/effect/src/Schedule.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Scheduler.ts index a60495eb0..816b27084 100644 --- a/repos/effect/packages/effect/src/Scheduler.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Schema.ts index de3428da8..1c4a323ad 100644 --- a/repos/effect/packages/effect/src/Schema.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaAST.ts index e9eaae850..4bf42ef43 100644 --- a/repos/effect/packages/effect/src/SchemaAST.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaGetter.ts index 2293489e9..84a1ced85 100644 --- a/repos/effect/packages/effect/src/SchemaGetter.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaIssue.ts index 07e95a426..6e32f0911 100644 --- a/repos/effect/packages/effect/src/SchemaIssue.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaParser.ts index 0ae10b550..f590b6e14 100644 --- a/repos/effect/packages/effect/src/SchemaParser.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaRepresentation.ts index 42387557e..d6153da6e 100644 --- a/repos/effect/packages/effect/src/SchemaRepresentation.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SchemaTransformation.ts index d02d894a8..7824aa0e3 100644 --- a/repos/effect/packages/effect/src/SchemaTransformation.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Scope.ts index 8e2cf1904..2746d1a2c 100644 --- a/repos/effect/packages/effect/src/Scope.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/ScopedRef.ts index af4a352f7..e8aa04951 100644 --- a/repos/effect/packages/effect/src/ScopedRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Semaphore.ts index eaa8e59c6..195513a22 100644 --- a/repos/effect/packages/effect/src/Semaphore.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Sink.ts index ddf3fdfe7..0074c1290 100644 --- a/repos/effect/packages/effect/src/Sink.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/StandardSchema.ts index 885765170..7108dc2a7 100644 --- a/repos/effect/packages/effect/src/StandardSchema.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Stdio.ts index d51caa541..858e533ea 100644 --- a/repos/effect/packages/effect/src/Stdio.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Stream.ts index 96eda628b..a6d1bcdce 100644 --- a/repos/effect/packages/effect/src/Stream.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/String.ts index dad0d3521..2391f1797 100644 --- a/repos/effect/packages/effect/src/String.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Struct.ts index b7ada252a..0125a79b3 100644 --- a/repos/effect/packages/effect/src/Struct.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SubscriptionRef.ts index 1a4adf732..4812904a0 100644 --- a/repos/effect/packages/effect/src/SubscriptionRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Symbol.ts index de444c25d..2df931e76 100644 --- a/repos/effect/packages/effect/src/Symbol.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/SynchronizedRef.ts index 4db44c41b..6c48dce84 100644 --- a/repos/effect/packages/effect/src/SynchronizedRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Take.ts index 48c2dd796..098d06253 100644 --- a/repos/effect/packages/effect/src/Take.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Terminal.ts index bbd046c52..3c5f5f24c 100644 --- a/repos/effect/packages/effect/src/Terminal.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Tracer.ts index 024c10568..0858f19d5 100644 --- a/repos/effect/packages/effect/src/Tracer.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Trie.ts index c9203a880..78d30100d 100644 --- a/repos/effect/packages/effect/src/Trie.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Tuple.ts index ecaeefbc6..9e9d5f21c 100644 --- a/repos/effect/packages/effect/src/Tuple.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxChunk.ts index 29342867a..e204c4b6f 100644 --- a/repos/effect/packages/effect/src/TxChunk.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxDeferred.ts index 681701f0a..f9530744f 100644 --- a/repos/effect/packages/effect/src/TxDeferred.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxHashMap.ts index 11352eab3..9fbb4d5ed 100644 --- a/repos/effect/packages/effect/src/TxHashMap.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxHashSet.ts index d95aa7a23..b6e31a439 100644 --- a/repos/effect/packages/effect/src/TxHashSet.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxPriorityQueue.ts index 2e42fdd52..2a865d796 100644 --- a/repos/effect/packages/effect/src/TxPriorityQueue.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxPubSub.ts index d31ee9cf9..2cfaac03d 100644 --- a/repos/effect/packages/effect/src/TxPubSub.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxQueue.ts index 1348abefe..8c9d80908 100644 --- a/repos/effect/packages/effect/src/TxQueue.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxReentrantLock.ts index cfe2c0117..ad4540862 100644 --- a/repos/effect/packages/effect/src/TxReentrantLock.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxRef.ts index d5a86f658..7f9b4c44e 100644 --- a/repos/effect/packages/effect/src/TxRef.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/TxSemaphore.ts index e42fd2631..83f648adb 100644 --- a/repos/effect/packages/effect/src/TxSemaphore.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/UndefinedOr.ts index eb9be8845..ff7e94f97 100644 --- a/repos/effect/packages/effect/src/UndefinedOr.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Unify.ts index a2e280985..5f7315ebe 100644 --- a/repos/effect/packages/effect/src/Unify.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Utils.ts index 9c9a3c83a..d35eff6c3 100644 --- a/repos/effect/packages/effect/src/Utils.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/Version.ts b/repos/effect/packages/effect/src/Version.ts new file mode 100644 index 000000000..4f5fde320 --- /dev/null +++ b/repos/effect/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/Chat.ts b/repos/effect/packages/effect/src/ai/Chat.ts index c55c69b41..f8c25ac36 100644 --- a/repos/effect/packages/effect/src/ai/Chat.ts +++ b/repos/effect/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/DecisionModel.ts b/repos/effect/packages/effect/src/ai/DecisionModel.ts index dd0bf6f8b..9c1d6602c 100644 --- a/repos/effect/packages/effect/src/ai/DecisionModel.ts +++ b/repos/effect/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/LanguageModel.ts b/repos/effect/packages/effect/src/ai/LanguageModel.ts index 6926ffc93..553bbe9fc 100644 --- a/repos/effect/packages/effect/src/ai/LanguageModel.ts +++ b/repos/effect/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/McpServer.ts b/repos/effect/packages/effect/src/ai/McpServer.ts index f76029cfb..6ed20a37e 100644 --- a/repos/effect/packages/effect/src/ai/McpServer.ts +++ b/repos/effect/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/Telemetry.ts b/repos/effect/packages/effect/src/ai/Telemetry.ts index 27fda52e2..4b39a2c71 100644 --- a/repos/effect/packages/effect/src/ai/Telemetry.ts +++ b/repos/effect/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/internal/mcpRuntime.ts b/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts index b388d1780..dbf891211 100644 --- a/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts +++ b/repos/effect/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/mcpStatefulRuntime.ts b/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts index 19c41ab07..bcede3100 100644 --- a/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts +++ b/repos/effect/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/cluster/K8sHttpClient.ts b/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts index 6e0a44159..7b5538053 100644 --- a/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts +++ b/repos/effect/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/encoding/Base64.ts b/repos/effect/packages/effect/src/encoding/Base64.ts index b04b5be45..012d4cb93 100644 --- a/repos/effect/packages/effect/src/encoding/Base64.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/encoding/Base64Url.ts index 01fb7d4d3..92486cb5f 100644 --- a/repos/effect/packages/effect/src/encoding/Base64Url.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/encoding/EncodingError.ts index c3ae39fff..c70a26d54 100644 --- a/repos/effect/packages/effect/src/encoding/EncodingError.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/encoding/Hex.ts index 1ac78464d..a0048d69b 100644 --- a/repos/effect/packages/effect/src/encoding/Hex.ts +++ b/repos/effect/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/Toml.ts b/repos/effect/packages/effect/src/encoding/Toml.ts index eae5c924c..b95d711e1 100644 --- a/repos/effect/packages/effect/src/encoding/Toml.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/encoding/Yaml.ts index ac8c23ab9..e553d226b 100644 --- a/repos/effect/packages/effect/src/encoding/Yaml.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/encoding/index.ts index 6b19d09e5..7ac4657b0 100644 --- a/repos/effect/packages/effect/src/encoding/index.ts +++ b/repos/effect/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/SqlEventJournal.ts b/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts index d434f4fd5..1c9c4437d 100644 --- a/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts +++ b/repos/effect/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/SqlEventLogServerUnencrypted.ts b/repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts index c3034c901..35a59f9d7 100644 --- a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts +++ b/repos/effect/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/http-api/HttpApiBuilder.ts b/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts index 0bad0d35c..21a033919 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/http-api/HttpApiClient.ts index abfed1134..6bf1a5bfc 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiClient.ts +++ b/repos/effect/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/HttpApiError.ts b/repos/effect/packages/effect/src/http-api/HttpApiError.ts index 87660b57b..cae629e79 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiError.ts +++ b/repos/effect/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/HttpApiSecurity.ts b/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts index 37a223182..98c8f61ec 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts +++ b/repos/effect/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/Headers.ts b/repos/effect/packages/effect/src/http/Headers.ts index a7c176cd4..0ec53bbcd 100644 --- a/repos/effect/packages/effect/src/http/Headers.ts +++ b/repos/effect/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/HttpClient.ts b/repos/effect/packages/effect/src/http/HttpClient.ts index 184bfb0ba..814dd8113 100644 --- a/repos/effect/packages/effect/src/http/HttpClient.ts +++ b/repos/effect/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/HttpClientResponse.ts b/repos/effect/packages/effect/src/http/HttpClientResponse.ts index 1e0769916..d8845b536 100644 --- a/repos/effect/packages/effect/src/http/HttpClientResponse.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/http/HttpEffect.ts index 7841d767e..16e0fb4e2 100644 --- a/repos/effect/packages/effect/src/http/HttpEffect.ts +++ b/repos/effect/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/HttpMiddleware.ts b/repos/effect/packages/effect/src/http/HttpMiddleware.ts index 4c53b9b09..0b818c4e1 100644 --- a/repos/effect/packages/effect/src/http/HttpMiddleware.ts +++ b/repos/effect/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/HttpServer.ts b/repos/effect/packages/effect/src/http/HttpServer.ts index 18eca5ba8..ef698bc08 100644 --- a/repos/effect/packages/effect/src/http/HttpServer.ts +++ b/repos/effect/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/internal/tracing.ts b/repos/effect/packages/effect/src/http/internal/tracing.ts new file mode 100644 index 000000000..138794d87 --- /dev/null +++ b/repos/effect/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/repos/effect/packages/effect/src/index.ts index 0ef040bba..73f60d5fb 100644 --- a/repos/effect/packages/effect/src/index.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/internal/arbitrary/array.ts index 85d3c1d08..6e5547b69 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/array.ts +++ b/repos/effect/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/model.ts b/repos/effect/packages/effect/src/internal/arbitrary/model.ts index f6d017433..b095cd028 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/model.ts +++ b/repos/effect/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/runner.ts b/repos/effect/packages/effect/src/internal/arbitrary/runner.ts index f9c68d284..05d3426ee 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/runner.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/internal/arbitrary/schema.ts index f739349cb..9183848c3 100644 --- a/repos/effect/packages/effect/src/internal/arbitrary/schema.ts +++ b/repos/effect/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/effect.ts b/repos/effect/packages/effect/src/internal/effect.ts index 874e545e1..f6343b78b 100644 --- a/repos/effect/packages/effect/src/internal/effect.ts +++ b/repos/effect/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/schedule.ts b/repos/effect/packages/effect/src/internal/schedule.ts index ecb1c3743..16e7dd591 100644 --- a/repos/effect/packages/effect/src/internal/schedule.ts +++ b/repos/effect/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/standardSchema.ts b/repos/effect/packages/effect/src/internal/schema/standardSchema.ts index 66147c958..d575bfe03 100644 --- a/repos/effect/packages/effect/src/internal/schema/standardSchema.ts +++ b/repos/effect/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/version.ts b/repos/effect/packages/effect/src/internal/version.ts new file mode 100644 index 000000000..dae214bd4 --- /dev/null +++ b/repos/effect/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/observability/OtlpExporter.ts b/repos/effect/packages/effect/src/observability/OtlpExporter.ts index 385d9555f..5b749b870 100644 --- a/repos/effect/packages/effect/src/observability/OtlpExporter.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/observability/OtlpLogger.ts index cb63bd3f0..fa5e97ce5 100644 --- a/repos/effect/packages/effect/src/observability/OtlpLogger.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/observability/OtlpMetrics.ts index f3cb68a4e..a9a8296ea 100644 --- a/repos/effect/packages/effect/src/observability/OtlpMetrics.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/observability/OtlpResource.ts index 4cbe45595..22a44be97 100644 --- a/repos/effect/packages/effect/src/observability/OtlpResource.ts +++ b/repos/effect/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/OtlpTracer.ts b/repos/effect/packages/effect/src/observability/OtlpTracer.ts index df341205d..c6f6197d5 100644 --- a/repos/effect/packages/effect/src/observability/OtlpTracer.ts +++ b/repos/effect/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/internal/otlpProtobuf.ts b/repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts index a068668f9..038defb31 100644 --- a/repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts +++ b/repos/effect/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/reactivity/AsyncResult.ts b/repos/effect/packages/effect/src/reactivity/AsyncResult.ts index 3a4a9673f..3869e5876 100644 --- a/repos/effect/packages/effect/src/reactivity/AsyncResult.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/reactivity/Atom.ts index ecfbab214..c5fea04eb 100644 --- a/repos/effect/packages/effect/src/reactivity/Atom.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts index ed10982fe..26be3e8e9 100644 --- a/repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts +++ b/repos/effect/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/AtomRegistry.ts b/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts index 91b1d0002..ef4af667c 100644 --- a/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts +++ b/repos/effect/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/rpc/RpcClient.ts b/repos/effect/packages/effect/src/rpc/RpcClient.ts index 3272af43e..615d168ad 100644 --- a/repos/effect/packages/effect/src/rpc/RpcClient.ts +++ b/repos/effect/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/RpcServer.ts b/repos/effect/packages/effect/src/rpc/RpcServer.ts index a6d06f709..bda39256d 100644 --- a/repos/effect/packages/effect/src/rpc/RpcServer.ts +++ b/repos/effect/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/internal/tracing.ts b/repos/effect/packages/effect/src/rpc/internal/tracing.ts new file mode 100644 index 000000000..4911da29e --- /dev/null +++ b/repos/effect/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/SchemaCompiler/runtime.ts b/repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts index 9ce8e2d7c..d2985054b 100644 --- a/repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts +++ b/repos/effect/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/sql/SqlClient.ts b/repos/effect/packages/effect/src/sql/SqlClient.ts index 3c71ebed9..61d39e97b 100644 --- a/repos/effect/packages/effect/src/sql/SqlClient.ts +++ b/repos/effect/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/Statement.ts b/repos/effect/packages/effect/src/sql/Statement.ts index f09348f6e..3829bc013 100644 --- a/repos/effect/packages/effect/src/sql/Statement.ts +++ b/repos/effect/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/testing/TestClock.ts b/repos/effect/packages/effect/src/testing/TestClock.ts index 394177642..d4a4951c8 100644 --- a/repos/effect/packages/effect/src/testing/TestClock.ts +++ b/repos/effect/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/repos/effect/packages/effect/src/testing/TestConsole.ts index b8a5a52f6..62fbe1a81 100644 --- a/repos/effect/packages/effect/src/testing/TestConsole.ts +++ b/repos/effect/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/index.ts b/repos/effect/packages/effect/src/testing/index.ts index aca10dddf..12aca49b5 100644 --- a/repos/effect/packages/effect/src/testing/index.ts +++ b/repos/effect/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/test/Array.test.ts b/repos/effect/packages/effect/test/Array.test.ts index 0c939d3d3..7021b62ef 100644 --- a/repos/effect/packages/effect/test/Array.test.ts +++ b/repos/effect/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/Cache.test.ts b/repos/effect/packages/effect/test/Cache.test.ts index 7776eedcb..e04c577df 100644 --- a/repos/effect/packages/effect/test/Cache.test.ts +++ b/repos/effect/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/Channel.test.ts b/repos/effect/packages/effect/test/Channel.test.ts index 3ff55a3f8..a005b99ab 100644 --- a/repos/effect/packages/effect/test/Channel.test.ts +++ b/repos/effect/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/Config.test.ts b/repos/effect/packages/effect/test/Config.test.ts index 3e9b37df7..1bc4f1db0 100644 --- a/repos/effect/packages/effect/test/Config.test.ts +++ b/repos/effect/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/Cron.test.ts b/repos/effect/packages/effect/test/Cron.test.ts index d4d942662..4f693f6a3 100644 --- a/repos/effect/packages/effect/test/Cron.test.ts +++ b/repos/effect/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/Effect.test.ts b/repos/effect/packages/effect/test/Effect.test.ts index 609e1f06b..e10953d51 100644 --- a/repos/effect/packages/effect/test/Effect.test.ts +++ b/repos/effect/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/ExecutionPlan.test.ts b/repos/effect/packages/effect/test/ExecutionPlan.test.ts index 905a5b653..e1fc96a7b 100644 --- a/repos/effect/packages/effect/test/ExecutionPlan.test.ts +++ b/repos/effect/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/Fiber.test.ts b/repos/effect/packages/effect/test/Fiber.test.ts index ea5ca64c1..b929f54c5 100644 --- a/repos/effect/packages/effect/test/Fiber.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/FiberHandle.test.ts index b0abfc16d..a247c752b 100644 --- a/repos/effect/packages/effect/test/FiberHandle.test.ts +++ b/repos/effect/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/Formatter.test.ts b/repos/effect/packages/effect/test/Formatter.test.ts index 9040fa030..f25972bad 100644 --- a/repos/effect/packages/effect/test/Formatter.test.ts +++ b/repos/effect/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/Graph.test.ts b/repos/effect/packages/effect/test/Graph.test.ts index f064afbe6..8347b1146 100644 --- a/repos/effect/packages/effect/test/Graph.test.ts +++ b/repos/effect/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/Layer.test.ts b/repos/effect/packages/effect/test/Layer.test.ts index 879f4cd52..9b987064c 100644 --- a/repos/effect/packages/effect/test/Layer.test.ts +++ b/repos/effect/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/Logger.test.ts b/repos/effect/packages/effect/test/Logger.test.ts index 78b8d45e1..e36628296 100644 --- a/repos/effect/packages/effect/test/Logger.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/ManagedRuntime.test.ts index 4b121cf6d..e52ad5059 100644 --- a/repos/effect/packages/effect/test/ManagedRuntime.test.ts +++ b/repos/effect/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/PartitionedSemaphore.test.ts b/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts index 1fcd5f287..83cb20275 100644 --- a/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts +++ b/repos/effect/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/Pool.test.ts b/repos/effect/packages/effect/test/Pool.test.ts index b3faee601..1dd1a8aeb 100644 --- a/repos/effect/packages/effect/test/Pool.test.ts +++ b/repos/effect/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/PubSub.test.ts b/repos/effect/packages/effect/test/PubSub.test.ts index 655aac0f5..cb07a1334 100644 --- a/repos/effect/packages/effect/test/PubSub.test.ts +++ b/repos/effect/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/Queue.test.ts b/repos/effect/packages/effect/test/Queue.test.ts index 2f90b9b57..6f4beea84 100644 --- a/repos/effect/packages/effect/test/Queue.test.ts +++ b/repos/effect/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/Request.test.ts b/repos/effect/packages/effect/test/Request.test.ts index cc6d9911c..05ca5b26f 100644 --- a/repos/effect/packages/effect/test/Request.test.ts +++ b/repos/effect/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/Scope.test.ts b/repos/effect/packages/effect/test/Scope.test.ts index 73dccd0d8..9b3becdbc 100644 --- a/repos/effect/packages/effect/test/Scope.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/ScopedCache.test.ts index 961afbf93..c6c312836 100644 --- a/repos/effect/packages/effect/test/ScopedCache.test.ts +++ b/repos/effect/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/Sink.test.ts b/repos/effect/packages/effect/test/Sink.test.ts index a63091c78..39c9e1a24 100644 --- a/repos/effect/packages/effect/test/Sink.test.ts +++ b/repos/effect/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/Stream.test.ts b/repos/effect/packages/effect/test/Stream.test.ts index e6933ce98..dc4b64634 100644 --- a/repos/effect/packages/effect/test/Stream.test.ts +++ b/repos/effect/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/Tracer.test.ts b/repos/effect/packages/effect/test/Tracer.test.ts index 899f3c80a..68e2eb3b1 100644 --- a/repos/effect/packages/effect/test/Tracer.test.ts +++ b/repos/effect/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/TxHashMap.test.ts b/repos/effect/packages/effect/test/TxHashMap.test.ts index bd4a5b132..a1469795a 100644 --- a/repos/effect/packages/effect/test/TxHashMap.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/TxHashSet.test.ts index 674c95083..44f0a909f 100644 --- a/repos/effect/packages/effect/test/TxHashSet.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/TxPriorityQueue.test.ts index dbc4702e6..660d4efc5 100644 --- a/repos/effect/packages/effect/test/TxPriorityQueue.test.ts +++ b/repos/effect/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/TxReentrantLock.test.ts b/repos/effect/packages/effect/test/TxReentrantLock.test.ts index c9884240d..30708a6e0 100644 --- a/repos/effect/packages/effect/test/TxReentrantLock.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/TxSemaphore.test.ts index 1e903272e..a80861070 100644 --- a/repos/effect/packages/effect/test/TxSemaphore.test.ts +++ b/repos/effect/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/Version.test.ts b/repos/effect/packages/effect/test/Version.test.ts new file mode 100644 index 000000000..381ff7ecc --- /dev/null +++ b/repos/effect/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/DecisionModel.test.ts b/repos/effect/packages/effect/test/ai/DecisionModel.test.ts index f76e60fe9..cb58a61fc 100644 --- a/repos/effect/packages/effect/test/ai/DecisionModel.test.ts +++ b/repos/effect/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/LanguageModel.test.ts b/repos/effect/packages/effect/test/ai/LanguageModel.test.ts index 65892245c..11b6f708a 100644 --- a/repos/effect/packages/effect/test/ai/LanguageModel.test.ts +++ b/repos/effect/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/McpServer/McpConformance/TransportsTest.ts b/repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts index 6134c6562..b83472054 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts +++ b/repos/effect/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/McpServer.test.ts b/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts index 4f235c711..05dd823f3 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts index 585b19140..1b2b7f382 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts index e08c8e092..249377617 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts +++ b/repos/effect/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/McpServerLayer.ts b/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts index 8995b0a51..625d38b68 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts +++ b/repos/effect/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/arbitrary/Arbitrary.causes.test.ts b/repos/effect/packages/effect/test/arbitrary/Arbitrary.causes.test.ts new file mode 100644 index 000000000..d012d5425 --- /dev/null +++ b/repos/effect/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.test.ts b/repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts index 7497ec591..8d609ef71 100644 --- a/repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts +++ b/repos/effect/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/cluster/K8sHttpClient.test.ts b/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts index d1649e9d4..28a071d83 100644 --- a/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts +++ b/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts @@ -1,8 +1,42 @@ import { assert, describe, it } from "@effect/vitest" -import { Effect, Schema } from "effect" +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*() { diff --git a/repos/effect/packages/effect/test/encoding/Toml.test.ts b/repos/effect/packages/effect/test/encoding/Toml.test.ts index 235087e37..5651a8ed7 100644 --- a/repos/effect/packages/effect/test/encoding/Toml.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/encoding/Yaml.test.ts index 49d1a238e..93809602e 100644 --- a/repos/effect/packages/effect/test/encoding/Yaml.test.ts +++ b/repos/effect/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/SqlEventLogServerUnencryptedStorageTest.ts b/repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts index 848445ffd..2f8fe3f1f 100644 --- a/repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts +++ b/repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts @@ -1,16 +1,17 @@ import { assert, it } from "@effect/vitest" -import { Effect, Fiber, Layer, Option, Queue, Stream } from "effect" +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 type * as SqlClient from "effect/sql/SqlClient" +import * as SqlClient from "effect/sql/SqlClient" let nextNamespace = 0 const uniqueNamespace = (prefix: string) => `${prefix}_${++nextNamespace}` -const makeOptions = (prefix: string) => { +export const makeOptions = (prefix: string) => { const namespace = uniqueNamespace(prefix) return { entryTablePrefix: `effect_events_${namespace}`, @@ -19,9 +20,9 @@ const makeOptions = (prefix: string) => { } } -const makeStoreId = (prefix: string) => `${uniqueNamespace(prefix)}_store` as StoreId +export const makeStoreId = (prefix: string) => `${uniqueNamespace(prefix)}_store` as StoreId -const makeEntry = ( +export const makeEntry = ( name: string, options: { readonly id?: EventJournal.EntryId | undefined @@ -35,7 +36,7 @@ const makeEntry = ( payload: new TextEncoder().encode(name) }, { disableChecks: true }) -const makeStorage = (options: { +export const makeStorage = (options: { readonly entryTablePrefix?: string readonly remoteIdTable?: string readonly insertBatchSize?: number @@ -44,6 +45,20 @@ const makeStorage = (options: { 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), @@ -71,13 +86,7 @@ export const suite = (name: string, layer: Layer.Layer entry.remoteSequence), [1, 2]) @@ -104,14 +113,7 @@ export const suite = (name: string, layer: Layer.Layer entry.entry.idString), [entryA1.idString]) @@ -166,4 +162,178 @@ export const suite = (name: string, layer: Layer.Layer + 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/http/HttpClient.test.ts b/repos/effect/packages/effect/test/http/HttpClient.test.ts index f0f77272e..74662c69b 100644 --- a/repos/effect/packages/effect/test/http/HttpClient.test.ts +++ b/repos/effect/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/HttpMiddleware.test.ts b/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts index bfe91c667..9d042244b 100644 --- a/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts +++ b/repos/effect/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/httpapi/HttpApiBuilder.test.ts b/repos/effect/packages/effect/test/httpapi/HttpApiBuilder.test.ts index 5c325a6e2..001714ebb 100644 --- a/repos/effect/packages/effect/test/httpapi/HttpApiBuilder.test.ts +++ b/repos/effect/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/HttpApiError.test.ts b/repos/effect/packages/effect/test/httpapi/HttpApiError.test.ts index 6517ec960..1f7bc8801 100644 --- a/repos/effect/packages/effect/test/httpapi/HttpApiError.test.ts +++ b/repos/effect/packages/effect/test/httpapi/HttpApiError.test.ts @@ -1,29 +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", () => { - it.effect("does not report payload schema-validation failures", () => - Effect.gen(function*() { - const schemaError = yield* HttpApiError.HttpApiSchemaError.wrap( - "Payload", - 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, "Payload") - assert.isTrue(Exit.isFailure(exit)) - if (Exit.isFailure(exit)) { - assert.strictEqual(Cause.squash(exit.cause), schemaError) - } - assert.deepStrictEqual(reports, []) - })) + 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/observability/OtlpConfig.test.ts b/repos/effect/packages/effect/test/observability/OtlpConfig.test.ts index 5c65f100b..1e84c8e77 100644 --- a/repos/effect/packages/effect/test/observability/OtlpConfig.test.ts +++ b/repos/effect/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/OtlpExporter.test.ts b/repos/effect/packages/effect/test/observability/OtlpExporter.test.ts index 76d973c7c..7ddb780bf 100644 --- a/repos/effect/packages/effect/test/observability/OtlpExporter.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/observability/OtlpLogger.test.ts b/repos/effect/packages/effect/test/observability/OtlpLogger.test.ts new file mode 100644 index 000000000..0e24f5719 --- /dev/null +++ b/repos/effect/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/repos/effect/packages/effect/test/observability/OtlpMetrics.test.ts index a18dcb0c8..6fd6f429b 100644 --- a/repos/effect/packages/effect/test/observability/OtlpMetrics.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/observability/OtlpResource.test.ts index b2c652dd5..a42ebd7f3 100644 --- a/repos/effect/packages/effect/test/observability/OtlpResource.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/observability/OtlpSerialization.test.ts b/repos/effect/packages/effect/test/observability/OtlpSerialization.test.ts index cff8a9466..42a37fe56 100644 --- a/repos/effect/packages/effect/test/observability/OtlpSerialization.test.ts +++ b/repos/effect/packages/effect/test/observability/OtlpSerialization.test.ts @@ -1,4 +1,6 @@ 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", () => { @@ -7,4 +9,51 @@ describe("OtlpSerialization", () => { 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/repos/effect/packages/effect/test/observability/OtlpTracer.test.ts b/repos/effect/packages/effect/test/observability/OtlpTracer.test.ts new file mode 100644 index 000000000..e9cecd6ea --- /dev/null +++ b/repos/effect/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/reactivity/Atom.test.ts b/repos/effect/packages/effect/test/reactivity/Atom.test.ts index cd281885d..0c77d2c89 100644 --- a/repos/effect/packages/effect/test/reactivity/Atom.test.ts +++ b/repos/effect/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/rpc/RpcClient.test.ts b/repos/effect/packages/effect/test/rpc/RpcClient.test.ts index 1ca9e1fa1..88c13947d 100644 --- a/repos/effect/packages/effect/test/rpc/RpcClient.test.ts +++ b/repos/effect/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/RpcServer.test.ts b/repos/effect/packages/effect/test/rpc/RpcServer.test.ts index 0322986ec..a39c094fa 100644 --- a/repos/effect/packages/effect/test/rpc/RpcServer.test.ts +++ b/repos/effect/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/Schema.test.ts b/repos/effect/packages/effect/test/schema/Schema.test.ts index a78cec290..9d35320b9 100644 --- a/repos/effect/packages/effect/test/schema/Schema.test.ts +++ b/repos/effect/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/SchemaCompilerConcurrency.test.ts b/repos/effect/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts index cc7437315..0aebabd27 100644 --- a/repos/effect/packages/effect/test/schema/SchemaCompilerConcurrency.test.ts +++ b/repos/effect/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/SchemaCompilerRegression.test.ts b/repos/effect/packages/effect/test/schema/SchemaCompilerRegression.test.ts index 2241bd8d3..91f0f7c82 100644 --- a/repos/effect/packages/effect/test/schema/SchemaCompilerRegression.test.ts +++ b/repos/effect/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/toEquivalence.test.ts b/repos/effect/packages/effect/test/schema/toEquivalence.test.ts index 5d34dd2a3..8b88901c5 100644 --- a/repos/effect/packages/effect/test/schema/toEquivalence.test.ts +++ b/repos/effect/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/toStandardJSONSchemaV1.test.ts b/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts index f57a476e7..6e4637e4a 100644 --- a/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts +++ b/repos/effect/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/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts index 5521a68ec..991354948 100644 --- a/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts +++ b/repos/effect/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/typetest/Channel.tst.ts b/repos/effect/packages/effect/typetest/Channel.tst.ts index 6031c4f99..8f0662bbd 100644 --- a/repos/effect/packages/effect/typetest/Channel.tst.ts +++ b/repos/effect/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/Effect.tst.ts b/repos/effect/packages/effect/typetest/Effect.tst.ts index 7efb01d51..1a4e6f7b4 100644 --- a/repos/effect/packages/effect/typetest/Effect.tst.ts +++ b/repos/effect/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/Sink.tst.ts b/repos/effect/packages/effect/typetest/Sink.tst.ts index 4ed4dd6b3..c5dff8cc8 100644 --- a/repos/effect/packages/effect/typetest/Sink.tst.ts +++ b/repos/effect/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/ai/McpServer.tst.ts b/repos/effect/packages/effect/typetest/ai/McpServer.tst.ts index d2f643809..54e972885 100644 --- a/repos/effect/packages/effect/typetest/ai/McpServer.tst.ts +++ b/repos/effect/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/http/HttpServer.tst.ts b/repos/effect/packages/effect/typetest/http/HttpServer.tst.ts new file mode 100644 index 000000000..e32e079d7 --- /dev/null +++ b/repos/effect/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/HttpApiClient.tst.ts b/repos/effect/packages/effect/typetest/httpapi/HttpApiClient.tst.ts index 707202bf7..76059e69f 100644 --- a/repos/effect/packages/effect/typetest/httpapi/HttpApiClient.tst.ts +++ b/repos/effect/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/reactivity/AsyncResult.tst.ts b/repos/effect/packages/effect/typetest/reactivity/AsyncResult.tst.ts index 6a838a9d0..07f4ad4fc 100644 --- a/repos/effect/packages/effect/typetest/reactivity/AsyncResult.tst.ts +++ b/repos/effect/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/repos/effect/packages/effect/typetest/reactivity/Atom.tst.ts b/repos/effect/packages/effect/typetest/reactivity/Atom.tst.ts index 8d61aa611..0950b56e7 100644 --- a/repos/effect/packages/effect/typetest/reactivity/Atom.tst.ts +++ b/repos/effect/packages/effect/typetest/reactivity/Atom.tst.ts @@ -1,4 +1,5 @@ -import { Layer } from "effect" +import { Effect, Layer, SubscriptionRef } from "effect" +import type { AsyncResult } from "effect/reactivity" import { Atom } from "effect/reactivity" import { describe, expect, it } from "tstyche" @@ -20,4 +21,23 @@ describe("Atom", () => { 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/repos/effect/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts index e39cc0c4c..92a032ef5 100644 --- a/repos/effect/packages/effect/typetest/reactivity/AtomHttpApi.tst.ts +++ b/repos/effect/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/schema/Union.tst.ts b/repos/effect/packages/effect/typetest/schema/Union.tst.ts index f7b0db136..0902592ea 100644 --- a/repos/effect/packages/effect/typetest/schema/Union.tst.ts +++ b/repos/effect/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/opentelemetry/CHANGELOG.md b/repos/effect/packages/opentelemetry/CHANGELOG.md index c51e721bd..4e22380c9 100644 --- a/repos/effect/packages/opentelemetry/CHANGELOG.md +++ b/repos/effect/packages/opentelemetry/CHANGELOG.md @@ -1,5 +1,25 @@ # @effect/opentelemetry +## 4.0.2 + +### Patch Changes + +- [#8769](https://github.com/Effect-TS/effect/pull/8769) [`166437b`](https://github.com/Effect-TS/effect/commit/166437bdb88030cd831e8a1fbff1a633ab36fb2b) Thanks @fubhy! - Omit cumulative `min` and `max` from delta histogram points after the first export. First-export and cumulative histogram extrema are unchanged. + +- [#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. + +- [#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. + +- [#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`. + +- [#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. +- 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/opentelemetry/package.json b/repos/effect/packages/opentelemetry/package.json index d9ae509e7..23942fd15 100644 --- a/repos/effect/packages/opentelemetry/package.json +++ b/repos/effect/packages/opentelemetry/package.json @@ -1,7 +1,7 @@ { "name": "@effect/opentelemetry", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "OpenTelemetry integration for Effect", "homepage": "https://effect.website", diff --git a/repos/effect/packages/opentelemetry/src/OtelLogger.ts b/repos/effect/packages/opentelemetry/src/OtelLogger.ts index eec4adfa0..477ef0bae 100644 --- a/repos/effect/packages/opentelemetry/src/OtelLogger.ts +++ b/repos/effect/packages/opentelemetry/src/OtelLogger.ts @@ -14,6 +14,7 @@ import { SeverityNumber } from "@opentelemetry/api-logs" import * as Otel from "@opentelemetry/sdk-logs" import type { NonEmptyReadonlyArray } from "effect/Array" import * as Arr from "effect/Array" +import * as Cause from "effect/Cause" import * as Clock from "effect/Clock" import * as Context from "effect/Context" import type * as Duration from "effect/Duration" @@ -25,6 +26,7 @@ import * as Predicate from "effect/Predicate" import * as Rec from "effect/Record" import * as References from "effect/References" import * as Tracer from "effect/Tracer" +import * as Version from "effect/Version" import { nanosToHrTime, unknownToAttributeValue } from "./internal/attributes.ts" import { Resource } from "./Resource.ts" @@ -88,17 +90,17 @@ export const make: Effect.Effect< > = Effect.gen(function*() { const loggerProvider = yield* OtelLoggerProvider const clock = yield* Clock.Clock - const otelLogger = loggerProvider.getLogger("@effect/opentelemetry") + const otelLogger = loggerProvider.getLogger("effect", Version.getCurrentVersion()) return Logger.make((options) => { - const attributes: Record = { - fiberId: options.fiber.id - } + const attributes: Record = {} for (const [key, value] of Object.entries(options.fiber.getRef(References.CurrentLogAnnotations))) { Rec.assignProperty(attributes, key, unknownToAttributeValue(value)) } + attributes["effect.fiberId"] = options.fiber.id + const span = Context.getOrUndefined(options.fiber.context, Tracer.ParentSpan) if (Predicate.isNotUndefined(span)) { @@ -107,18 +109,25 @@ export const make: Effect.Effect< } const now = options.date.getTime() + // Outermost spans win duplicate labels. for (const [label, startTime] of options.fiber.getRef(References.CurrentLogSpans)) { - attributes[`logSpan.${label}`] = `${now - startTime}ms` + attributes[`effect.log_span.${label}`] = now - startTime + } + + 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).map(unknownToAttributeValue) - const hrTime = nanosToHrTime(clock.currentTimeNanosUnsafe()) otelLogger.emit({ body: message.length === 1 ? message[0] : message, severityText: options.logLevel, severityNumber: logLevelToSeverityNumber(options.logLevel), - timestamp: hrTime, - observedTimestamp: hrTime, + timestamp: options.date, + observedTimestamp: nanosToHrTime(clock.currentTimeNanosUnsafe()), attributes }) }) diff --git a/repos/effect/packages/opentelemetry/src/OtelTracer.ts b/repos/effect/packages/opentelemetry/src/OtelTracer.ts index dd787c21b..6115a7ce4 100644 --- a/repos/effect/packages/opentelemetry/src/OtelTracer.ts +++ b/repos/effect/packages/opentelemetry/src/OtelTracer.ts @@ -10,17 +10,17 @@ * OpenTelemetry tracer provider or an explicitly provided `OtelTracer`. This * module does not create exporters or span processors by itself, so spans are * exported only when the provider has been configured by the application or by - * the Node/Web SDK layers. Parentage is taken from Effect spans first and can - * also attach to the active OpenTelemetry context, while `makeExternalSpan` and - * `withSpanContext` are the entry points for continuing an incoming remote - * trace. Preserve `traceFlags` and `traceState` when building external spans; - * otherwise sampling defaults to sampled and trace state cannot be propagated. + * the Node/Web SDK layers. Effect parents take precedence over active + * OpenTelemetry spans. Use `Effect.withParentSpan` to inherit Effect-installed + * ambient spans. + * `makeExternalSpan` and `withSpanContext` continue incoming remote traces. + * Preserve `traceFlags` and `traceState` when building external spans; otherwise + * sampling defaults to sampled and trace state cannot be propagated. * * @stability unstable * @since 4.0.0 */ import * as Otel from "@opentelemetry/api" -import * as OtelSemConv from "@opentelemetry/semantic-conventions" import * as Cause from "effect/Cause" import type * as Clock from "effect/Clock" import * as Context from "effect/Context" @@ -31,8 +31,8 @@ import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" import * as Tracer from "effect/Tracer" +import * as Version from "effect/Version" import { nanosToHrTime, recordToAttributes, unknownToAttributeValue } from "./internal/attributes.ts" -import { Resource } from "./Resource.ts" // ============================================================================= // Service Definitions @@ -110,14 +110,15 @@ export const make: Effect.Effect = Effect.map( ) }, context(primitive, fiber) { - const currentSpan = fiber.cache.span + const currentSpan = getPropagatedSpan(fiber.cache.span) if (currentSpan === undefined) { return primitive["~effect/Effect/evaluate"](fiber) } + const context = populateContext(Otel.context.active(), currentSpan) return Otel.context.with( - populateContext(Otel.context.active(), currentSpan), + context.setValue(EffectSpanKey, Otel.trace.getSpan(context)), () => primitive["~effect/Effect/evaluate"](fiber) ) } @@ -178,32 +179,29 @@ export const layerGlobalProvider: Layer.Layer = Layer.sync( ) /** - * Layer that creates an OpenTelemetry tracer from the provided tracer provider and resource metadata. + * Layer that creates an OpenTelemetry tracer from the provided tracer provider, + * using `effect` and the current Effect version as the instrumentation scope. * * @stability unstable * @category layers * @since 4.0.0 */ -export const layerTracer: Layer.Layer = Layer.effect( +export const layerTracer: Layer.Layer = Layer.effect( OtelTracer, Effect.gen(function*() { - const resource = yield* Resource const provider = yield* OtelTracerProvider - return provider.getTracer( - resource.attributes[OtelSemConv.ATTR_SERVICE_NAME] as string, - resource.attributes[OtelSemConv.ATTR_SERVICE_VERSION] as string - ) + return provider.getTracer("effect", Version.getCurrentVersion()) }) ) /** - * Layer that creates an OpenTelemetry tracer from the global tracer provider and the current resource. + * Layer that creates an OpenTelemetry tracer from the global tracer provider. * * @stability unstable * @category layers * @since 4.0.0 */ -export const layerGlobalTracer: Layer.Layer = layerTracer.pipe( +export const layerGlobalTracer: Layer.Layer = layerTracer.pipe( Layer.provide(layerGlobalProvider) ) @@ -214,7 +212,7 @@ export const layerGlobalTracer: Layer.Layer = layer * @category layers * @since 4.0.0 */ -export const layerGlobal: Layer.Layer = Layer.effect(Tracer.Tracer, make).pipe( +export const layerGlobal: Layer.Layer = Layer.effect(Tracer.Tracer, make).pipe( Layer.provideMerge(layerGlobalTracer) ) @@ -228,12 +226,12 @@ export const layerGlobal: Layer.Layer = Layer.effec export const layerWithoutOtelTracer: Layer.Layer = Layer.effect(Tracer.Tracer, make) /** - * Layer that creates an OpenTelemetry tracer from a provider and resource, then installs it as the Effect tracer. + * Layer that creates an OpenTelemetry tracer from a provider, then installs it as the Effect tracer. * * **When to use** * - * Use when you already provide an `OtelTracerProvider` and a `Resource`, and - * want Effect spans backed by a tracer derived from them. + * Use when you already provide an `OtelTracerProvider` and want Effect spans + * backed by a tracer derived from it. * * @see {@link layerTracer} for creating only the OpenTelemetry tracer service * @see {@link layerGlobal} for installing the Effect tracer from the global provider @@ -243,7 +241,7 @@ export const layerWithoutOtelTracer: Layer.Layer = Lay * @category layers * @since 4.0.0 */ -export const layer: Layer.Layer = layerWithoutOtelTracer.pipe( +export const layer: Layer.Layer = layerWithoutOtelTracer.pipe( Layer.provideMerge(layerTracer) ) @@ -490,12 +488,7 @@ export class OtelSpan implements Tracer.Span { this.span.setStatus({ code: Otel.SpanStatusCode.OK }) } else { if (Cause.hasInterruptsOnly(exit.cause)) { - this.span.setStatus({ - code: Otel.SpanStatusCode.OK, - message: Cause.pretty(exit.cause) - }) - this.span.setAttribute("span.label", "⚠︎ Interrupted") - this.span.setAttribute("status.interrupted", true) + this.span.setAttribute("effect.fiber.interrupted", true) } else { const errors = Cause.prettyErrors(exit.cause, { includeCauseInStack: true @@ -509,7 +502,6 @@ export class OtelSpan implements Tracer.Span { message: errors[0].message }) } else { - // empty cause means no error this.span.setStatus({ code: Otel.SpanStatusCode.OK }) } } @@ -534,13 +526,18 @@ class OtelParentSpanContext extends Context.Service< Otel.SpanContext >()("@effect/opentelemetry/Tracer/OtelParentSpanContext") {} +// Prevents re-entry from inheriting Effect-installed spans. +const EffectSpanKey = Otel.createContextKey("@effect/opentelemetry/OtelTracer/EffectSpan") + const getOtelParent = ( tracer: Otel.TraceAPI, context: Otel.Context, annotations: Context.Context ): Option.Option => { - const otelParent = tracer.getSpanContext(context) - if (!otelParent) return Option.none() + const otelSpan = tracer.getSpan(context) + if (otelSpan === undefined || otelSpan === context.getValue(EffectSpanKey)) return Option.none() + const otelParent = otelSpan.spanContext() + if (!tracer.isSpanContextValid(otelParent)) return Option.none() return Option.some(Tracer.externalSpan({ spanId: otelParent.spanId, traceId: otelParent.traceId, @@ -615,6 +612,13 @@ const extractTraceService = ( return Context.getOrUndefined(parent.annotations, service) } +const getPropagatedSpan = (span: Tracer.AnySpan | undefined): Tracer.AnySpan | undefined => { + while (span !== undefined && Context.get(span.annotations, Tracer.DisablePropagation)) { + span = span._tag === "Span" ? Option.getOrUndefined(span.parent) : undefined + } + return span +} + const populateContext = ( context: Otel.Context, span: Tracer.AnySpan, diff --git a/repos/effect/packages/opentelemetry/src/Resource.ts b/repos/effect/packages/opentelemetry/src/Resource.ts index b7a74f585..14a9fc91d 100644 --- a/repos/effect/packages/opentelemetry/src/Resource.ts +++ b/repos/effect/packages/opentelemetry/src/Resource.ts @@ -20,6 +20,7 @@ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as Rec from "effect/Record" +import * as Version from "effect/Version" /** * Service tag for OpenTelemetry metadata attached to emitted telemetry. @@ -55,8 +56,16 @@ export const layer = (config: { Resources.resourceFromAttributes(configToAttributes(config)) ) +const sdkAttributes = (): Record => ({ + [OtelSemConv.ATTR_TELEMETRY_SDK_NAME]: "@effect/opentelemetry", + [OtelSemConv.ATTR_TELEMETRY_SDK_LANGUAGE]: typeof (globalThis as any).document === "undefined" + ? OtelSemConv.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS + : OtelSemConv.TELEMETRY_SDK_LANGUAGE_VALUE_WEBJS, + [OtelSemConv.ATTR_TELEMETRY_SDK_VERSION]: Version.getCurrentVersion() +}) + /** - * Converts resource configuration into OpenTelemetry attributes, adding service name, optional service version, and telemetry SDK metadata. + * Converts service metadata into OpenTelemetry attributes with SDK defaults. * * **When to use** * @@ -66,14 +75,8 @@ export const layer = (config: { * * **Details** * - * The returned record copies `attributes` first, then sets `service.name`, - * `telemetry.sdk.name`, and `telemetry.sdk.language`. `service.version` is - * included only when `serviceVersion` is provided. - * - * **Gotchas** - * - * Custom values for `service.name` and `telemetry.sdk.*` are overwritten by this - * helper. An empty `serviceVersion` is treated as absent. + * Custom attributes override `telemetry.sdk.*` defaults. Service options override + * matching attributes. An empty `serviceVersion` is treated as absent. * * @see {@link layer} for creating a `Resource` layer from explicit metadata * @see {@link layerFromEnv} for merging attributes with OpenTelemetry environment variables @@ -88,12 +91,9 @@ export const configToAttributes = (options: { readonly attributes?: OtelApi.Attributes }): Record => { const attributes: Record = { + ...sdkAttributes(), ...(options.attributes ?? undefined), - [OtelSemConv.ATTR_SERVICE_NAME]: options.serviceName, - [OtelSemConv.ATTR_TELEMETRY_SDK_NAME]: "@effect/opentelemetry", - [OtelSemConv.ATTR_TELEMETRY_SDK_LANGUAGE]: typeof (globalThis as any).document === "undefined" - ? OtelSemConv.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS - : OtelSemConv.TELEMETRY_SDK_LANGUAGE_VALUE_WEBJS + [OtelSemConv.ATTR_SERVICE_NAME]: options.serviceName } if (options.serviceVersion) { attributes[OtelSemConv.ATTR_SERVICE_VERSION] = options.serviceVersion @@ -102,7 +102,8 @@ export const configToAttributes = (options: { } /** - * Creates a `Resource` layer from OpenTelemetry environment variables, optionally merging additional attributes. + * Creates a `Resource` layer from SDK defaults, then environment variables, + * then additional attributes (later values take precedence). * * @stability unstable * @category layers @@ -135,6 +136,7 @@ export const layerFromEnv = ( attributes[OtelSemConv.ATTR_SERVICE_NAME] = serviceName.value } return Resources.resourceFromAttributes({ + ...sdkAttributes(), ...attributes, ...additionalAttributes }) diff --git a/repos/effect/packages/opentelemetry/src/index.ts b/repos/effect/packages/opentelemetry/src/index.ts index 51790a8ee..9443f18c4 100644 --- a/repos/effect/packages/opentelemetry/src/index.ts +++ b/repos/effect/packages/opentelemetry/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/opentelemetry/src/internal/attributes.ts b/repos/effect/packages/opentelemetry/src/internal/attributes.ts index d8fa20915..326146370 100644 --- a/repos/effect/packages/opentelemetry/src/internal/attributes.ts +++ b/repos/effect/packages/opentelemetry/src/internal/attributes.ts @@ -24,6 +24,16 @@ export const unknownToAttributeValue = (value: unknown): Otel.AttributeValue => return value } else if (typeof value === "bigint") { return value.toString() + } else if (Array.isArray(value) && isHomogeneousPrimitiveArray(value)) { + return value.slice() as Otel.AttributeValue } return Inspectable.toStringUnknown(value) } + +const isHomogeneousPrimitiveArray = ( + array: ReadonlyArray +): array is Array | Array | Array => { + const type = typeof array[0] + return (array.length === 0 || type === "string" || type === "number" || type === "boolean") && + array.every((item) => typeof item === type) +} diff --git a/repos/effect/packages/opentelemetry/src/internal/metrics.ts b/repos/effect/packages/opentelemetry/src/internal/metrics.ts index eae58e7f7..4aa6d17ea 100644 --- a/repos/effect/packages/opentelemetry/src/internal/metrics.ts +++ b/repos/effect/packages/opentelemetry/src/internal/metrics.ts @@ -26,8 +26,6 @@ interface PreviousHistogramState { readonly count: number readonly sum: number readonly bucketCounts: ReadonlyArray - readonly min: number - readonly max: number } interface PreviousSummaryState { @@ -195,8 +193,7 @@ export class MetricProducerImpl implements MetricProducer { let reportCount = state.state.count let reportSum = state.state.sum let reportBucketCounts = currentBuckets.counts - const reportMin = state.state.min - const reportMax = state.state.max + let reportExtrema = true if (isDelta) { const previousState = this.previousHistogramState.get(metricKey) @@ -206,13 +203,14 @@ export class MetricProducerImpl implements MetricProducer { reportBucketCounts = currentBuckets.counts.map((c, i) => Math.max(0, c - (previousState.bucketCounts[i] ?? 0)) ) + // Interval extrema cannot be derived from cumulative min / max, + // so omit the optional fields rather than report stale values + reportExtrema = false } this.previousHistogramState.set(metricKey, { count: state.state.count, sum: state.state.sum, - bucketCounts: currentBuckets.counts.slice(), - min: state.state.min, - max: state.state.max + bucketCounts: currentBuckets.counts.slice() }) } @@ -228,8 +226,7 @@ export class MetricProducerImpl implements MetricProducer { counts: reportBucketCounts }, count: reportCount, - min: reportMin, - max: reportMax, + ...(reportExtrema ? { min: state.state.min, max: state.state.max } : undefined), sum: reportSum } } diff --git a/repos/effect/packages/opentelemetry/test/OtelLogger.test.ts b/repos/effect/packages/opentelemetry/test/OtelLogger.test.ts index 2045f5046..3a6565435 100644 --- a/repos/effect/packages/opentelemetry/test/OtelLogger.test.ts +++ b/repos/effect/packages/opentelemetry/test/OtelLogger.test.ts @@ -5,12 +5,74 @@ import { assert, describe, it } from "@effect/vitest" import { SeverityNumber } from "@opentelemetry/api-logs" import { InMemoryLogRecordExporter, type LogRecordProcessor, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs" import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base" +import * as Cause from "effect/Cause" import * as Clock from "effect/Clock" +import * as ConfigProvider from "effect/ConfigProvider" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as References from "effect/References" +import { TestClock } from "effect/testing" +import * as Version from "effect/Version" describe("Logger", () => { + const makeTestLayer = (exporter: InMemoryLogRecordExporter) => + NodeSdk.layer(Effect.sync(() => ({ + resource: { serviceName: "test", serviceVersion: "service-version" }, + logRecordProcessor: [new SimpleLogRecordProcessor({ exporter })] + }))) + + it.effect("adds SDK resource defaults when the resource comes only from the environment", () => { + const exporter = new InMemoryLogRecordExporter() + return Effect.gen(function*() { + yield* Effect.log("test") + const record = exporter.getFinishedLogRecords()[0]! + assert.strictEqual(record.instrumentationScope.name, "effect") + assert.strictEqual(record.instrumentationScope.version, Version.getCurrentVersion()) + assert.deepStrictEqual(record.resource.attributes, { + "service.name": "env-service", + "telemetry.sdk.name": "@effect/opentelemetry", + "telemetry.sdk.language": "nodejs", + "telemetry.sdk.version": Version.getCurrentVersion() + }) + }).pipe( + Effect.provide(NodeSdk.layer(() => ({ logRecordProcessor: [new SimpleLogRecordProcessor({ exporter })] }))), + Effect.provideService( + ConfigProvider.ConfigProvider, + ConfigProvider.fromEnv({ env: { OTEL_SERVICE_NAME: "env-service" } }) + ) + ) + }) + + it.effect("namespaces generated attributes and lets them override annotations", () => { + const exporter = new InMemoryLogRecordExporter() + 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" + }) + ) + + const attributes = exporter.getFinishedLogRecords()[0]!.attributes + assert.strictEqual(attributes["effect.fiberId"], fiber.id) + assert.strictEqual(attributes["effect.log_span.op"], 5) + assert.strictEqual(attributes["exception.type"], "TypeError") + assert.strictEqual(attributes["exception.message"], "cause message") + assert.include(attributes["exception.stacktrace"], "nested failure") + }).pipe(Effect.provide(makeTestLayer(exporter))) + }) + it.effect("shuts down the logger provider after forceFlush rejects", () => Effect.gen(function*() { let shutdowns = 0 @@ -75,7 +137,7 @@ describe("Logger", () => { ) }) - it.effect("uses wall-clock timestamps and keeps them aligned with spans", () => { + it.effect("keeps event time separate from observed time and uses wall-clock span time", () => { const logExporter = new InMemoryLogRecordExporter() const spanExporter = new InMemorySpanExporter() const wallTimeNanos = 1_735_689_600_123_456_789n @@ -114,9 +176,9 @@ describe("Logger", () => { const log = logs[0]! const span = spans[0]! - assert.deepStrictEqual(log.hrTime, expectedTime) + assert.deepStrictEqual(log.hrTime, [0, 1_000_000]) assert.deepStrictEqual(log.hrTimeObserved, expectedTime) - assert.deepStrictEqual(log.hrTime, span.startTime) + assert.deepStrictEqual(log.hrTimeObserved, span.startTime) assert.strictEqual(log.attributes.spanId, span.spanContext().spanId) assert.strictEqual(log.attributes.traceId, span.spanContext().traceId) }).pipe( diff --git a/repos/effect/packages/opentelemetry/test/OtelMetrics.test.ts b/repos/effect/packages/opentelemetry/test/OtelMetrics.test.ts index df8947529..531079e7e 100644 --- a/repos/effect/packages/opentelemetry/test/OtelMetrics.test.ts +++ b/repos/effect/packages/opentelemetry/test/OtelMetrics.test.ts @@ -363,4 +363,47 @@ describe("Metrics", () => { const secondValue = (secondResult.resourceMetrics.scopeMetrics[0]!.metrics[0] as any).dataPoints[0].value assert.deepStrictEqual([firstValue, secondValue], [1, 1]) }).pipe(Effect.provideService(Metric.MetricRegistry, new Map()))) + + it.effect.each(["delta", "cumulative"] as const)( + "histogram extrema with %s temporality", + (temporality) => + Effect.gen(function*() { + const services = yield* Effect.context() + const producer = new internal.MetricProducerImpl(resourceFromAttributes({}), services, temporality) + const histogram = Metric.histogram("latency", { + boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 3 }) + }) + + yield* Metric.update(histogram, 1) + yield* Metric.update(histogram, 100) + const first = findMetric(yield* Effect.promise(() => producer.collect()), "latency").dataPoints[0].value + assert.deepStrictEqual(first, { + buckets: { boundaries: [50], counts: [1, 1] }, + count: 2, + sum: 101, + min: 1, + max: 100 + }) + + yield* Metric.update(histogram, 50) + yield* Metric.update(histogram, 60) + const second = findMetric(yield* Effect.promise(() => producer.collect()), "latency").dataPoints[0].value + assert.deepStrictEqual( + second, + temporality === "delta" + ? { + buckets: { boundaries: [50], counts: [1, 1] }, + count: 2, + sum: 110 + } + : { + buckets: { boundaries: [50], counts: [2, 2] }, + count: 4, + sum: 211, + min: 1, + max: 100 + } + ) + }).pipe(Effect.provideService(Metric.MetricRegistry, new Map())) + ) }) diff --git a/repos/effect/packages/opentelemetry/test/OtelTracer.test.ts b/repos/effect/packages/opentelemetry/test/OtelTracer.test.ts index ef21f0a00..0c3e7bf7b 100644 --- a/repos/effect/packages/opentelemetry/test/OtelTracer.test.ts +++ b/repos/effect/packages/opentelemetry/test/OtelTracer.test.ts @@ -1,5 +1,6 @@ import * as NodeSdk from "@effect/opentelemetry/NodeSdk" import * as OtelTracer from "@effect/opentelemetry/OtelTracer" +import * as Resource from "@effect/opentelemetry/Resource" import { assert, describe, it } from "@effect/vitest" import * as OtelApi from "@opentelemetry/api" import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks" @@ -7,21 +8,36 @@ import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-tr import * as Cause from "effect/Cause" import * as EffectContext from "effect/Context" import * as Effect from "effect/Effect" +import * as Exit from "effect/Exit" +import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as EffectTracer from "effect/Tracer" +import * as Version from "effect/Version" -const TracingLayer = NodeSdk.layer(Effect.sync(() => ({ - resource: { - serviceName: "test" - }, - spanProcessor: [new SimpleSpanProcessor(new InMemorySpanExporter())] -}))) +const TracingLayer = OtelTracer.layer.pipe( + Layer.provide(NodeSdk.layerTracerProvider([new SimpleSpanProcessor(new InMemorySpanExporter())])), + Layer.provide(Resource.layerEmpty) +) // needed to test context propagation const contextManager = new AsyncHooksContextManager() -OtelApi.context.setGlobalContextManager(contextManager) +OtelApi.context.setGlobalContextManager(contextManager.enable()) describe("Tracer", () => { + it.effect("uses the shared Effect version as the tracer instrumentation scope", () => { + const exporter = new InMemorySpanExporter() + const layer = NodeSdk.layer(Effect.sync(() => ({ + resource: { serviceName: "test", serviceVersion: "service-version" }, + spanProcessor: [new SimpleSpanProcessor(exporter)] + }))) + return Effect.gen(function*() { + yield* Effect.void.pipe(Effect.withSpan("test")) + const scope = exporter.getFinishedSpans()[0]!.instrumentationScope + assert.strictEqual(scope.name, "effect") + assert.strictEqual(scope.version, Version.getCurrentVersion()) + }).pipe(Effect.provide(layer)) + }) + describe("provided", () => { it.effect("withSpan", () => Effect.gen(function*() { @@ -59,6 +75,54 @@ describe("Tracer", () => { Effect.provide(TracingLayer) )) + it.effect("inherits an OpenTelemetry span started inside an Effect span", () => + Effect.gen(function*() { + const services = yield* Effect.context() + const tracer = yield* OtelTracer.OtelTracer + yield* Effect.promise(() => + tracer.startActiveSpan("otel-parent", async (parent) => { + try { + const child = await Effect.runPromise(Effect.currentSpan.pipe( + Effect.withSpan("child"), + Effect.provideContext(services) + )) + + assert.strictEqual(child.traceId, parent.spanContext().traceId) + assert.strictEqual(Option.getOrThrow(child.parent).spanId, parent.spanContext().spanId) + } finally { + parent.end() + } + }) + ).pipe(Effect.withSpan("outer")) + }).pipe(Effect.provide(TracingLayer))) + + it.effect("does not inherit an ambient Effect span when re-entering from JavaScript", () => + Effect.gen(function*() { + const services = yield* Effect.context() + yield* Effect.gen(function*() { + const outer = yield* Effect.currentSpan + const child = yield* Effect.promise(() => + Effect.runPromise(Effect.currentSpan.pipe( + Effect.withSpan("child"), + Effect.provideContext(services) + )) + ) + + assert.isTrue(Option.isNone(child.parent)) + assert.notStrictEqual(child.traceId, outer.traceId) + }).pipe(Effect.withSpan("outer")) + }).pipe(Effect.provide(TracingLayer))) + + it.effect("a propagation-disabled Effect span is not a parent", () => + Effect.gen(function*() { + const child = yield* Effect.currentSpan.pipe( + Effect.withSpan("child"), + Effect.withSpan("disabled", { annotations: EffectTracer.DisablePropagation.context(true) }) + ) + + assert.isTrue(Option.isNone(child.parent)) + }).pipe(Effect.provide(TracingLayer))) + it.effect("supervisor sets context", () => Effect.sync(() => { const context = OtelApi.context.active() @@ -234,6 +298,55 @@ describe("Tracer", () => { assert.include(stacktrace as string, "[cause]: Error: inner cause") })) + it.effect("leaves interruption Unset with effect.fiber.interrupted", () => { + const exporter = new InMemorySpanExporter() + return Effect.gen(function*() { + const span = yield* Effect.makeSpan("test") + span.end(span.status.startTime + 1n, Exit.interrupt()) + const spans = exporter.getFinishedSpans() + assert.lengthOf(spans, 1) + assert.deepStrictEqual(spans[0].status, { code: OtelApi.SpanStatusCode.UNSET }) + assert.deepStrictEqual(spans[0].events, []) + assert.deepStrictEqual(spans[0].attributes, { "effect.fiber.interrupted": true }) + }).pipe(Effect.provide(NodeSdk.layer(() => ({ + resource: { serviceName: "test" }, + spanProcessor: [new SimpleSpanProcessor(exporter)] + })))) + }) + it.effect("exports homogeneous primitive arrays as array attributes", () => + Effect.gen(function*() { + const exporter = new InMemorySpanExporter() + const spanProcessor = new SimpleSpanProcessor(exporter) + + yield* Effect.void.pipe( + Effect.withSpan("array-span", { + attributes: { + strings: ["a", "b"], + numbers: [1, 2], + booleans: [true, false], + mixed: [1, "a"] + } + }), + Effect.andThen(Effect.never), // keep the exporter alive + Effect.provide(NodeSdk.layer(() => ({ + resource: { + serviceName: "test" + }, + spanProcessor: [spanProcessor] + }))), + Effect.forkChild({ startImmediately: true }) + ) + + const spanData = exporter.getFinishedSpans()[0] + if (spanData === undefined) { + return yield* Effect.die("Missing span data") + } + assert.deepStrictEqual(spanData.attributes.strings, ["a", "b"]) + assert.deepStrictEqual(spanData.attributes.numbers, [1, 2]) + assert.deepStrictEqual(spanData.attributes.booleans, [true, false]) + assert.isString(spanData.attributes.mixed) + })) + it.effect("withSpanContext", () => Effect.gen(function*() { const effect = Effect.gen(function*() { diff --git a/repos/effect/packages/opentelemetry/test/Resource.test.ts b/repos/effect/packages/opentelemetry/test/Resource.test.ts new file mode 100644 index 000000000..9a4ef2e24 --- /dev/null +++ b/repos/effect/packages/opentelemetry/test/Resource.test.ts @@ -0,0 +1,24 @@ +import * as Resource from "@effect/opentelemetry/Resource" +import { assert, describe, it } from "@effect/vitest" + +describe("Resource", () => { + it("lets custom attributes override SDK defaults but not the service options", () => { + const attributes = Resource.configToAttributes({ + serviceName: "test", + serviceVersion: "service-version", + attributes: { + "service.name": "attribute-service", + "telemetry.sdk.name": "custom", + "telemetry.sdk.language": "webjs", + "telemetry.sdk.version": "custom-version" + } + }) + assert.deepStrictEqual(attributes, { + "service.name": "test", + "service.version": "service-version", + "telemetry.sdk.name": "custom", + "telemetry.sdk.language": "webjs", + "telemetry.sdk.version": "custom-version" + }) + }) +}) diff --git a/repos/effect/packages/platform/browser/CHANGELOG.md b/repos/effect/packages/platform/browser/CHANGELOG.md index 3bbe10f1b..7618ec4e1 100644 --- a/repos/effect/packages/platform/browser/CHANGELOG.md +++ b/repos/effect/packages/platform/browser/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/platform-browser +## 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/platform/browser/package.json b/repos/effect/packages/platform/browser/package.json index d0c922fd4..13f94018a 100644 --- a/repos/effect/packages/platform/browser/package.json +++ b/repos/effect/packages/platform/browser/package.json @@ -1,7 +1,7 @@ { "name": "@effect/platform-browser", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Platform specific implementations for the browser", "homepage": "https://effect.website", diff --git a/repos/effect/packages/platform/browser/src/BrowserCrypto.ts b/repos/effect/packages/platform/browser/src/BrowserCrypto.ts index 3a8913540..5722f32bb 100644 --- a/repos/effect/packages/platform/browser/src/BrowserCrypto.ts +++ b/repos/effect/packages/platform/browser/src/BrowserCrypto.ts @@ -6,6 +6,7 @@ * browser programs can use the standard implementation while tests or embedded * runtimes can provide their own `Crypto` object. * + * @stability unstable * @since 1.0.0 */ import * as Context from "effect/Context" @@ -22,6 +23,7 @@ import * as PlatformError from "effect/PlatformError" * Use to override the browser `Crypto` object used by the platform crypto * layer. * + * @stability unstable * @category services * @since 1.0.0 */ @@ -48,6 +50,7 @@ export const WebCrypto = Context.Reference("@effect/platform-browser/Cry * fail with `PlatformError` when `crypto.subtle.digest` is unavailable or the * browser rejects the digest request. * + * @stability unstable * @category layers * @since 1.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserHttpClient.ts b/repos/effect/packages/platform/browser/src/BrowserHttpClient.ts index 7ba23d8ff..6274c0d8c 100644 --- a/repos/effect/packages/platform/browser/src/BrowserHttpClient.ts +++ b/repos/effect/packages/platform/browser/src/BrowserHttpClient.ts @@ -8,6 +8,7 @@ * XHR response type, an overridable `XMLHttpRequest` constructor service, and * the `layerXMLHttpRequest` layer. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -39,6 +40,7 @@ export { /** * Context reference for the `fetch` implementation used by the fetch-based HTTP client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -46,6 +48,7 @@ export { /** * Layer that provides an `HttpClient` implementation backed by the configured `Fetch` function. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -58,6 +61,7 @@ export { * Use to provide default credentials, cache, redirect, integrity, or other * fetch options for browser HTTP requests. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -71,6 +75,7 @@ export { /** * Allowed response body modes for the browser XHR HTTP client. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -87,6 +92,7 @@ export type XHRResponseType = "arraybuffer" | "text" * @see {@link XHRResponseType} for the allowed response body modes * @see {@link withXHRArrayBuffer} for scoping XHR response handling to `ArrayBuffer` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -98,6 +104,7 @@ export const CurrentXHRResponseType: Context.Reference = Contex /** * Runs an effect with `CurrentXHRResponseType` set to `"arraybuffer"` so the XHR HTTP client receives response bodies as `ArrayBuffer` values. * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -113,6 +120,7 @@ export const withXHRArrayBuffer = ( /** * Service tag for the `XMLHttpRequest` constructor used by the browser XHR HTTP client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -443,6 +451,7 @@ class ClientResponseImpl extends IncomingMessageImpl = KeyVa /** * Creates a `KeyValueStore` layer that uses the browser's `sessionStorage` API and stores values only for the current session. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -59,6 +62,7 @@ export const layerSessionStorage: Layer.Layer = Key * @see {@link layerLocalStorage} for synchronous persistent Web Storage * @see {@link layerSessionStorage} for synchronous tab-session Web Storage * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserPersistence.ts b/repos/effect/packages/platform/browser/src/BrowserPersistence.ts index 8075cc530..aa72d8536 100644 --- a/repos/effect/packages/platform/browser/src/BrowserPersistence.ts +++ b/repos/effect/packages/platform/browser/src/BrowserPersistence.ts @@ -7,6 +7,7 @@ * returning stored data. The database name can be customized and defaults to * `"effect_persistence"`. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "effect/Array" @@ -38,6 +39,7 @@ import * as Persistence from "effect/persistence/Persistence" * * @see {@link layerIndexedDb} for providing the higher-level `Persistence` service * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -73,6 +75,7 @@ const storeIdIndexName = "storeId" /** * Creates a `Persistence` layer backed by IndexedDB, optionally using the provided database name. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserRuntime.ts b/repos/effect/packages/platform/browser/src/BrowserRuntime.ts index c02dad8c0..5e0df15eb 100644 --- a/repos/effect/packages/platform/browser/src/BrowserRuntime.ts +++ b/repos/effect/packages/platform/browser/src/BrowserRuntime.ts @@ -5,6 +5,7 @@ * It forwards runner options to the core runtime and adds a `pagehide` * listener that interrupts the main fiber when the document is discarded. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -30,6 +31,7 @@ import { makeRunMain, type Teardown } from "effect/Runtime" * The `pagehide` interruption is best-effort. Browser teardown may prevent * asynchronous finalizers, network work, timers, or prompts from completing. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserSocket.ts b/repos/effect/packages/platform/browser/src/BrowserSocket.ts index 4278f6e64..a1f68b732 100644 --- a/repos/effect/packages/platform/browser/src/BrowserSocket.ts +++ b/repos/effect/packages/platform/browser/src/BrowserSocket.ts @@ -5,6 +5,7 @@ * the browser `WebSocket` constructor. `layerWebSocketConstructor` provides * only the browser-backed constructor service for lower-level socket code. * + * @stability unstable * @since 4.0.0 */ import type * as Duration from "effect/Duration" @@ -32,6 +33,7 @@ import * as Socket from "effect/socket/Socket" * * @see {@link layerWebSocketConstructor} for providing only the browser constructor service * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const layerWebSocket = (url: string, options?: { /** * Layer that provides a `WebSocketConstructor` service backed by `globalThis.WebSocket`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserStream.ts b/repos/effect/packages/platform/browser/src/BrowserStream.ts index 56d7333c8..bedabed30 100644 --- a/repos/effect/packages/platform/browser/src/BrowserStream.ts +++ b/repos/effect/packages/platform/browser/src/BrowserStream.ts @@ -5,6 +5,7 @@ * and `document.addEventListener` events into Effect `Stream` values. Both * helpers accept the usual listener options and an optional stream buffer size. * + * @stability unstable * @since 4.0.0 */ @@ -19,6 +20,7 @@ import * as Stream from "effect/Stream" * buffer size by passing an object as the second argument with the `bufferSize` * field. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -41,6 +43,7 @@ export const fromEventListenerWindow = ( * buffer size by passing an object as the second argument with the `bufferSize` * field. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserWorker.ts b/repos/effect/packages/platform/browser/src/BrowserWorker.ts index ed1681abe..47cb82286 100644 --- a/repos/effect/packages/platform/browser/src/BrowserWorker.ts +++ b/repos/effect/packages/platform/browser/src/BrowserWorker.ts @@ -6,6 +6,7 @@ * protocol. `layer` combines that platform with a `Spawner` built from a * callback that creates or returns the worker endpoint for each worker id. * + * @stability unstable * @since 4.0.0 */ import * as Deferred from "effect/Deferred" @@ -35,6 +36,7 @@ import { WorkerError, WorkerReceiveError } from "effect/workers/WorkerError" * * @see {@link layerPlatform} for providing only the browser worker platform * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -49,6 +51,7 @@ export const layer = ( /** * Layer that provides the browser `WorkerPlatform` for `Worker`, `SharedWorker`, and `MessagePort` communication. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/BrowserWorkerRunner.ts b/repos/effect/packages/platform/browser/src/BrowserWorkerRunner.ts index 58e0e1bd7..c2b049c10 100644 --- a/repos/effect/packages/platform/browser/src/BrowserWorkerRunner.ts +++ b/repos/effect/packages/platform/browser/src/BrowserWorkerRunner.ts @@ -7,6 +7,7 @@ * receives parent or client messages, runs Effect handlers, and posts responses * back through the browser messaging channel. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -32,6 +33,7 @@ if (typeof self !== "undefined" && "onconnect" in self) { /** * Creates a `WorkerRunnerPlatform` service that runs worker handlers over a `MessagePort` or `Window`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -183,6 +185,7 @@ export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatf * @see {@link make} for constructing a runner platform from an explicit endpoint * @see {@link layerMessagePort} for providing a platform from an explicit endpoint * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -193,6 +196,7 @@ export const layer: Layer.Layer = Layer.sync( /** * Layer that provides a `WorkerRunnerPlatform` using the supplied `MessagePort` or `Window`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/Clipboard.ts b/repos/effect/packages/platform/browser/src/Clipboard.ts index a68c41937..13e00d319 100644 --- a/repos/effect/packages/platform/browser/src/Clipboard.ts +++ b/repos/effect/packages/platform/browser/src/Clipboard.ts @@ -7,6 +7,7 @@ * supports reading and writing text, reading and writing `ClipboardItem` * payloads, writing one `Blob`, and clearing the clipboard. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -38,6 +39,7 @@ const ErrorTypeId = "~@effect/platform-browser/Clipboard/ClipboardError" * MIME type support varies by browser. Failed browser operations are surfaced * as `ClipboardError`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -54,6 +56,7 @@ export interface Clipboard { /** * Tagged error raised when a browser clipboard operation fails. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -75,6 +78,7 @@ export class ClipboardError extends Data.TaggedError("ClipboardError")<{ * @see {@link make} for building a custom clipboard service * @see {@link layer} for providing the browser-backed clipboard service * + * @stability unstable * @category services * @since 4.0.0 */ @@ -83,6 +87,7 @@ export const Clipboard: Context.Service = Context.Service< /** * Builds a `Clipboard` service from primitive read and write operations, deriving `clear` and `writeBlob` helpers. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -99,6 +104,7 @@ export const make = ( /** * Layer that directly interfaces with the browser Clipboard API. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/Geolocation.ts b/repos/effect/packages/platform/browser/src/Geolocation.ts index dc687bee6..b2eeb6ef6 100644 --- a/repos/effect/packages/platform/browser/src/Geolocation.ts +++ b/repos/effect/packages/platform/browser/src/Geolocation.ts @@ -8,6 +8,7 @@ * `PermissionDenied`, or `Timeout` reasons. The module also provides the * browser-backed layer and a `watchPosition` accessor. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -44,6 +45,7 @@ const ErrorTypeId = "~@effect/platform-browser/Geolocation/GeolocationError" * @see {@link GeolocationError} for represented browser geolocation failures * @see {@link layer} for the browser-backed service implementation * + * @stability unstable * @category services * @since 4.0.0 */ @@ -71,6 +73,7 @@ export interface Geolocation { * * @see {@link layer} for providing the browser-backed geolocation service * + * @stability unstable * @category services * @since 4.0.0 */ @@ -79,6 +82,7 @@ export const Geolocation: Context.Service = Context.Se /** * Tagged error wrapping a browser geolocation failure reason. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -104,6 +108,7 @@ export class GeolocationError extends Data.TaggedError("GeolocationError")<{ /** * Error reason for the browser geolocation `POSITION_UNAVAILABLE` failure. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -118,6 +123,7 @@ export class PositionUnavailable extends Data.TaggedError("PositionUnavailable") /** * Error reason for the browser geolocation `PERMISSION_DENIED` failure. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -132,6 +138,7 @@ export class PermissionDenied extends Data.TaggedError("PermissionDenied")<{ /** * Error reason for the browser geolocation `TIMEOUT` failure. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -146,6 +153,7 @@ export class Timeout extends Data.TaggedError("Timeout")<{ /** * Union of browser geolocation error reasons represented by the service. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -193,6 +201,7 @@ const makeQueue = ( /** * Layer that provides `Geolocation` using `navigator.geolocation`, with watched positions buffered in a sliding queue. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -217,6 +226,7 @@ export const layer: Layer.Layer = Layer.succeed( * Reads geolocation positions from the `Geolocation` service as a stream, with * an optional sliding buffer size. * + * @stability unstable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/IndexedDb.ts b/repos/effect/packages/platform/browser/src/IndexedDb.ts index aaa96d079..2119268ff 100644 --- a/repos/effect/packages/platform/browser/src/IndexedDb.ts +++ b/repos/effect/packages/platform/browser/src/IndexedDb.ts @@ -7,6 +7,7 @@ * wiring those primitives from `window`, and schemas for the key shapes accepted * by IndexedDB object stores and indexes. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -19,6 +20,7 @@ const TypeId = "~@effect/platform-browser/IndexedDb" /** * Service interface that provides the browser `indexedDB` factory and `IDBKeyRange` constructor. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -31,6 +33,7 @@ export interface IndexedDb { /** * Service tag for browser IndexedDB primitives. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -51,6 +54,7 @@ const IDBFlatKey = Schema.Union([ /** * Schema for IndexedDB keys: strings, non-NaN numbers, valid dates, buffer sources, or arrays of those flat key values. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -71,6 +75,7 @@ export const IDBValidKey = Schema.Union([IDBFlatKey, Schema.Array(IDBFlatKey)]) * * @see {@link IDBValidKey} for the broader IndexedDB key schema * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -85,6 +90,7 @@ export const AutoIncrement = Schema.Int.check( /** * Creates an `IndexedDb` service from an `IDBFactory` and `IDBKeyRange` constructor. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -93,6 +99,7 @@ export const make = (impl: Omit): IndexedDb => Indexed /** * Layer that provides `IndexedDb` from `window.indexedDB` and `window.IDBKeyRange`, failing with a config error when they are unavailable. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/IndexedDbDatabase.ts b/repos/effect/packages/platform/browser/src/IndexedDbDatabase.ts index b32c72b27..484d94a76 100644 --- a/repos/effect/packages/platform/browser/src/IndexedDbDatabase.ts +++ b/repos/effect/packages/platform/browser/src/IndexedDbDatabase.ts @@ -8,6 +8,7 @@ * Migration transactions can create or delete object stores and indexes, and * database failures are represented as `IndexedDbDatabaseError` values. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -71,6 +72,7 @@ const SchemaProto = { /** * String union describing the failure categories for IndexedDB database opening, migration, and schema operations. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -86,6 +88,7 @@ export type ErrorReason = /** * Tagged error for IndexedDB database operations, carrying a database error reason and the original cause. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -126,6 +129,7 @@ export class IndexedDbDatabaseError extends Data.TaggedError( * @see {@link IndexedDb.IndexedDb} for the lower-level browser IndexedDB primitives * @see {@link make} for creating a schema that provides this service as a layer * + * @stability unstable * @category services * @since 4.0.0 */ @@ -142,6 +146,7 @@ export class IndexedDbDatabase extends Context.Service< /** * Describes an IndexedDB schema version and its migrations, and acts as an effect that yields a query builder for the target version. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -195,6 +200,7 @@ export interface IndexedDbSchema< /** * Query builder available during a database migration, extended with object-store and index management helpers for the active `IDBTransaction`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -234,6 +240,7 @@ export interface Transaction< /** * Extracts the string-literal index names defined by an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -245,6 +252,7 @@ export type IndexFromTable = IsString /** * Extracts the valid index names for a table name within an IndexedDB version. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -258,6 +266,7 @@ export type IndexFromTableName< /** * Type-erased IndexedDB schema shape used when traversing schema migration chains. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -275,6 +284,7 @@ export interface Any { /** * Type-erased `IndexedDbSchema` covering any source version, target version, and migration error type. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -287,6 +297,7 @@ export type AnySchema = IndexedDbSchema< /** * Creates the initial `IndexedDbSchema` from a version and an initialization migration run during database upgrade. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/IndexedDbQueryBuilder.ts b/repos/effect/packages/platform/browser/src/IndexedDbQueryBuilder.ts index 13042897d..191bf0921 100644 --- a/repos/effect/packages/platform/browser/src/IndexedDbQueryBuilder.ts +++ b/repos/effect/packages/platform/browser/src/IndexedDbQueryBuilder.ts @@ -9,6 +9,7 @@ * stored rows with the table schema, and writes encode input values before * sending them to IndexedDB. * + * @stability unstable * @since 4.0.0 */ import type { NonEmptyReadonlyArray } from "effect/Array" @@ -56,6 +57,7 @@ const CommonProto = { /** * String union describing IndexedDB query failure categories such as decoding, encoding, and transaction errors. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -76,6 +78,7 @@ export type ErrorReason = * * @see {@link ErrorReason} for the supported failure categories * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -98,6 +101,7 @@ export class IndexedDbQueryError extends Data.TaggedError( /** * Typed query builder for an IndexedDB version, with helpers for table queries, database access, clearing data, and running effects in a shared transaction. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -146,6 +150,7 @@ export interface IndexedDbQueryBuilder< /** * Valid key-path type for a table schema, using encoded fields whose values are IndexedDB-valid keys. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -156,6 +161,7 @@ export type KeyPath = /** * Valid numeric key-path type for a table schema, used for auto-increment key paths. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -166,6 +172,7 @@ export type KeyPathNumber = /** * Namespace containing the typed IndexedDB query model interfaces and helper types. * + * @stability unstable * @since 4.0.0 */ export declare namespace IndexedDbQuery { @@ -724,6 +731,7 @@ export declare namespace IndexedDbQuery { /** * Service tag for the active `IDBTransaction` used to share a transaction across IndexedDB query effects. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -2043,6 +2051,7 @@ const awaitTransaction = (transaction: globalThis.IDBTransaction) => /** * Creates an `IndexedDbQueryBuilder` from an open database reference, key-range constructor, table map, and reactivity service. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/IndexedDbTable.ts b/repos/effect/packages/platform/browser/src/IndexedDbTable.ts index 12be0e987..a030c9dea 100644 --- a/repos/effect/packages/platform/browser/src/IndexedDbTable.ts +++ b/repos/effect/packages/platform/browser/src/IndexedDbTable.ts @@ -9,6 +9,7 @@ * * @see {@link make} for constructing table descriptors. * + * @stability unstable * @since 4.0.0 */ import { type Pipeable, pipeArguments } from "effect/Pipeable" @@ -23,6 +24,7 @@ const TypeId = "~@effect/platform-browser/IndexedDbTable" /** * Typed IndexedDB table definition containing its name, schema, key path, indexes, auto-increment setting, and transaction durability. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -52,6 +54,7 @@ export interface IndexedDbTable< /** * Schema constraint for table schemas that expose struct fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -62,6 +65,7 @@ export type AnySchemaStruct = Schema.Top & { /** * Type-erased shape of an `IndexedDbTable` used when table type parameters are not needed. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -80,6 +84,7 @@ export interface Any { /** * Type-erased `IndexedDbTable` retaining the table interface properties with broad type parameters. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -94,6 +99,7 @@ export type AnyWithProps = IndexedDbTable< /** * Extracts the table name type from an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -101,6 +107,7 @@ export type TableName
= Table["tableName"] /** * Extracts the key-path type from an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -109,6 +116,7 @@ export type KeyPath
= Table["keyPath"] /** * Extracts the auto-increment flag type from an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -117,6 +125,7 @@ export type AutoIncrement
= Table["autoIncrement"] /** * Extracts the schema type from an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -124,6 +133,7 @@ export type TableSchema
= Table["tableSchema"] /** * Extracts the decoding or encoding service requirements needed by an `IndexedDbTable` schema. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -134,6 +144,7 @@ export type Context
= /** * Extracts the encoded row type from an `IndexedDbTable` schema. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -142,6 +153,7 @@ export type Encoded
= Table["tableSchema"]["Encoded"] /** * Extracts the index definition map from an `IndexedDbTable`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -150,6 +162,7 @@ export type Indexes
= Table["indexes"] /** * Selects the table with the given name from a union of `IndexedDbTable` types. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -189,6 +202,7 @@ const Proto = { * * @see `IndexedDbVersion.make` for grouping table definitions into a schema version * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/IndexedDbVersion.ts b/repos/effect/packages/platform/browser/src/IndexedDbVersion.ts index ebc3b2358..9c2897706 100644 --- a/repos/effect/packages/platform/browser/src/IndexedDbVersion.ts +++ b/repos/effect/packages/platform/browser/src/IndexedDbVersion.ts @@ -15,6 +15,7 @@ * that should be queryable in each target version, avoid duplicate table names, and remember that key-path or * auto-increment changes usually require creating a new object store and copying data during the upgrade transaction. * + * @stability unstable * @since 4.0.0 */ import type { NonEmptyReadonlyArray } from "effect/Array" @@ -27,6 +28,7 @@ const TypeId = "~@effect/platform-browser/IndexedDbVersion" /** * Typed IndexedDB version definition containing the tables available in that schema version. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -41,6 +43,7 @@ export interface IndexedDbVersion< /** * Type-erased shape of an `IndexedDbVersion`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -51,6 +54,7 @@ export interface Any { /** * Type-erased `IndexedDbVersion` retaining version properties with broad table types. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -59,6 +63,7 @@ export type AnyWithProps = IndexedDbVersion /** * Extracts the table union from an `IndexedDbVersion`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -67,6 +72,7 @@ export type Tables = Db extends IndexedDbVersion /** * Selects a table by name from an `IndexedDbVersion`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -78,6 +84,7 @@ export type TableWithName< /** * Extracts the schema for a named table within an `IndexedDbVersion`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -125,6 +132,7 @@ const makeProto = (options: { * * @see {@link IndexedDbTable.make} for creating table definitions consumed by this constructor * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/Permissions.ts b/repos/effect/packages/platform/browser/src/Permissions.ts index b58b7e46f..a27f2144e 100644 --- a/repos/effect/packages/platform/browser/src/Permissions.ts +++ b/repos/effect/packages/platform/browser/src/Permissions.ts @@ -7,6 +7,7 @@ * are represented as `PermissionsError` values with `InvalidStateError` or * `TypeError` reasons, and `layer` provides the browser-backed service. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -21,6 +22,7 @@ const ErrorTypeId = "~@effect/platform-browser/Permissions/PermissionsError" * Wrapper on the Permission API (`navigator.permissions`) with methods for * querying status of permissions. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -43,6 +45,7 @@ export interface Permissions { /** * Error reason for an `InvalidStateError` raised by the browser Permissions API. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -57,6 +60,7 @@ export class PermissionsInvalidStateError extends Data.TaggedError("InvalidState /** * Error reason for a `TypeError` raised by the browser Permissions API. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -71,6 +75,7 @@ export class PermissionsTypeError extends Data.TaggedError("TypeError")<{ /** * Union of browser Permissions API error reasons represented by the service. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -79,6 +84,7 @@ export type PermissionsErrorReason = PermissionsInvalidStateError | PermissionsT /** * Tagged error wrapping a browser Permissions API failure reason. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -107,6 +113,7 @@ export class PermissionsError extends Data.TaggedError("PermissionsError")<{ * Use when you need to require or provide browser permission querying through * Effect's context. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -125,6 +132,7 @@ export const Permissions: Context.Service = Context.Se * `query` delegates to `navigator.permissions.query({ name })` and wraps * rejected browser operations in `PermissionsError`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/browser/src/index.ts b/repos/effect/packages/platform/browser/src/index.ts index b5dd50824..cd37e3016 100644 --- a/repos/effect/packages/platform/browser/src/index.ts +++ b/repos/effect/packages/platform/browser/src/index.ts @@ -1,90 +1,108 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 1.0.0 */ export * as BrowserCrypto from "./BrowserCrypto.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserHttpClient from "./BrowserHttpClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserKeyValueStore from "./BrowserKeyValueStore.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserPersistence from "./BrowserPersistence.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserRuntime from "./BrowserRuntime.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserSocket from "./BrowserSocket.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserStream from "./BrowserStream.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserWorker from "./BrowserWorker.ts" /** + * @stability unstable * @since 4.0.0 */ export * as BrowserWorkerRunner from "./BrowserWorkerRunner.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Clipboard from "./Clipboard.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Geolocation from "./Geolocation.ts" /** + * @stability unstable * @since 4.0.0 */ export * as IndexedDb from "./IndexedDb.ts" /** + * @stability unstable * @since 4.0.0 */ export * as IndexedDbDatabase from "./IndexedDbDatabase.ts" /** + * @stability unstable * @since 4.0.0 */ export * as IndexedDbQueryBuilder from "./IndexedDbQueryBuilder.ts" /** + * @stability unstable * @since 4.0.0 */ export * as IndexedDbTable from "./IndexedDbTable.ts" /** + * @stability unstable * @since 4.0.0 */ export * as IndexedDbVersion from "./IndexedDbVersion.ts" /** + * @stability unstable * @since 4.0.0 */ export * as Permissions from "./Permissions.ts" diff --git a/repos/effect/packages/platform/bun/CHANGELOG.md b/repos/effect/packages/platform/bun/CHANGELOG.md index 8deaf5e6e..51793399a 100644 --- a/repos/effect/packages/platform/bun/CHANGELOG.md +++ b/repos/effect/packages/platform/bun/CHANGELOG.md @@ -1,5 +1,16 @@ # @effect/platform-bun +## 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. + +- [#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. +- 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), [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519)]: + - effect@4.0.2 + - @effect/platform-node-shared@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/platform/bun/package.json b/repos/effect/packages/platform/bun/package.json index 6183072ee..dda2c30f5 100644 --- a/repos/effect/packages/platform/bun/package.json +++ b/repos/effect/packages/platform/bun/package.json @@ -1,7 +1,7 @@ { "name": "@effect/platform-bun", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Platform specific implementations for the Bun runtime", "homepage": "https://effect.website", diff --git a/repos/effect/packages/platform/bun/src/BunChildProcessSpawner.ts b/repos/effect/packages/platform/bun/src/BunChildProcessSpawner.ts index 62d7e5855..ba6f2fbe4 100644 --- a/repos/effect/packages/platform/bun/src/BunChildProcessSpawner.ts +++ b/repos/effect/packages/platform/bun/src/BunChildProcessSpawner.ts @@ -1,6 +1,7 @@ /** * Bun implementation of `ChildProcessSpawner`. * + * @stability unstable * @since 4.0.0 */ export * from "@effect/platform-node-shared/NodeChildProcessSpawner" diff --git a/repos/effect/packages/platform/bun/src/BunClusterHttp.ts b/repos/effect/packages/platform/bun/src/BunClusterHttp.ts index 2fc517348..ef794c2f1 100644 --- a/repos/effect/packages/platform/bun/src/BunClusterHttp.ts +++ b/repos/effect/packages/platform/bun/src/BunClusterHttp.ts @@ -6,6 +6,7 @@ * choosing serialization, runner health checks, runner storage, message * storage, and optional client-only mode from the supplied options. * + * @stability unstable * @since 4.0.0 */ import * as HttpRunner from "effect/cluster/HttpRunner" @@ -39,6 +40,7 @@ export { /** * Layer that provides a Kubernetes HTTP client for runner health checks. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -48,6 +50,7 @@ export { /** * Layer that provides a Bun HTTP server for cluster runners. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -94,6 +97,7 @@ export const layerHttpServer: Layer.Layer< * @see {@link layerHttpServer} for the server layer used by non-client-only transports * @see {@link layerK8sHttpClient} for Kubernetes runner health support * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunClusterSocket.ts b/repos/effect/packages/platform/bun/src/BunClusterSocket.ts index 9d6db8307..67223cdff 100644 --- a/repos/effect/packages/platform/bun/src/BunClusterSocket.ts +++ b/repos/effect/packages/platform/bun/src/BunClusterSocket.ts @@ -7,6 +7,7 @@ * re-exports the shared socket client and server protocol layers and provides * `layerK8sHttpClient` for Kubernetes runner health checks. * + * @stability unstable * @since 4.0.0 */ import { layerClientProtocol, layerSocketServer } from "@effect/platform-node-shared/NodeClusterSocket" @@ -36,6 +37,7 @@ export { * Provides the cluster `RpcClientProtocol` using the shared socket client * implementation. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -44,6 +46,7 @@ export { * Provides the socket server used by Bun cluster runners through the shared * socket server implementation. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -53,6 +56,7 @@ export { /** * Creates Bun socket cluster layers, configuring serialization, storage, runner health, and optional client-only mode. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -134,6 +138,7 @@ export const layer = < /** * Layer that provides `K8sHttpClient`, using the Kubernetes service-account CA certificate when it is available. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunCrypto.ts b/repos/effect/packages/platform/bun/src/BunCrypto.ts index 3fa34bac1..dad0ce9d9 100644 --- a/repos/effect/packages/platform/bun/src/BunCrypto.ts +++ b/repos/effect/packages/platform/bun/src/BunCrypto.ts @@ -9,6 +9,7 @@ * present for interoperability with existing protocols, not for new * security-sensitive designs. * + * @stability unstable * @since 1.0.0 */ import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto" @@ -18,6 +19,7 @@ import type * as Layer from "effect/Layer" /** * Layer that provides the Bun Crypto service implementation. * + * @stability unstable * @category layers * @since 1.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunFileSystem.ts b/repos/effect/packages/platform/bun/src/BunFileSystem.ts index 8eb2efcef..14bbf8a27 100644 --- a/repos/effect/packages/platform/bun/src/BunFileSystem.ts +++ b/repos/effect/packages/platform/bun/src/BunFileSystem.ts @@ -5,6 +5,7 @@ * the shared Node file-system implementation. Once the layer is provided, * programs use the standard `effect/FileSystem` service operations. * + * @stability unstable * @since 4.0.0 */ import * as NodeFileSystem from "@effect/platform-node-shared/NodeFileSystem" @@ -14,6 +15,7 @@ import type * as Layer from "effect/Layer" /** * Layer that provides the `FileSystem` service for Bun using the shared Node file-system implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunHttpClient.ts b/repos/effect/packages/platform/bun/src/BunHttpClient.ts index 23d51c8aa..742051bcd 100644 --- a/repos/effect/packages/platform/bun/src/BunHttpClient.ts +++ b/repos/effect/packages/platform/bun/src/BunHttpClient.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunHttpPlatform.ts b/repos/effect/packages/platform/bun/src/BunHttpPlatform.ts index be892cf4a..b9f2e4dc7 100644 --- a/repos/effect/packages/platform/bun/src/BunHttpPlatform.ts +++ b/repos/effect/packages/platform/bun/src/BunHttpPlatform.ts @@ -7,6 +7,7 @@ * provides the Bun file-system layer and ETag generator required by * `HttpPlatform`. * + * @stability unstable * @since 4.0.0 */ import * as NodeHttpCompression from "@effect/platform-node-shared/NodeHttpCompression" @@ -60,6 +61,7 @@ const make: Effect.Effect< /** * Layer that provides the Bun `HttpPlatform`, including file responses backed by `Bun.file`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunHttpServer.ts b/repos/effect/packages/platform/bun/src/BunHttpServer.ts index ca1cb83bc..1a413ef56 100644 --- a/repos/effect/packages/platform/bun/src/BunHttpServer.ts +++ b/repos/effect/packages/platform/bun/src/BunHttpServer.ts @@ -9,6 +9,7 @@ * for the server alone, the Bun HTTP support services, the combined server, * configurable server options, and a test server with an HTTP client. * + * @stability unstable * @since 4.0.0 */ import type { Server as BunServer, ServerWebSocket } from "bun" @@ -60,6 +61,7 @@ import * as BunStream from "./BunStream.ts" /** * Bun serve options accepted by the HTTP server, extended with typed route definitions. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -85,6 +87,7 @@ export type ServeOptions = * that is compressed when per-message deflate is negotiated. It defaults to * 1024, matching the default threshold of Node's `ws` server. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -106,6 +109,7 @@ export type WebSocketOptions = /** * Creates a scoped Bun `HttpServer` from `Bun.serve` options, stopping the server on scope finalization with optional graceful shutdown settings. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -189,7 +193,9 @@ export const make = Effect.fnUntraced( const httpEffect = HttpEffect.toHandled(httpApp, (request, response) => Effect.sync(() => { - ;(request as BunServerRequest).resolve(makeResponse(request, response, services, scope)) + const bunRequest = request as BunServerRequest + bunRequest.resolve(makeResponse(request, response, services, scope)) + if (upgradedSources.has(bunRequest.source)) return upgradedResponse }), middleware) function handler(request: Request, server: BunServer) { @@ -227,6 +233,12 @@ export const make = Effect.fnUntraced( const MIN_COMPRESSIBLE_SIZE = 1024 +// Reported to middleware in place of the handler's discarded response. +const upgradedResponse = ServerResponse.empty({ status: 101 }) + +// Keyed by source so request copies from `modify` share the upgrade state. +const upgradedSources = new WeakSet() + const makeResponse = ( request: ServerRequest.HttpServerRequest, response: ServerResponse.HttpServerResponse, @@ -294,6 +306,7 @@ const makeResponse = ( /** * Layer that provides only `HttpServer` by constructing a scoped Bun server from the supplied serve options. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -308,6 +321,7 @@ export const layerServer: ( /** * Layer that provides Bun HTTP support services: `HttpPlatform`, weak ETag generation, and `BunServices`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -324,6 +338,7 @@ export const layerHttpServices: Layer.Layer< /** * Layer that provides a Bun `HttpServer` together with the Bun HTTP platform, ETag generator, and Bun services. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -344,6 +359,7 @@ export const layer = ( /** * Layer that starts a Bun HTTP server on an ephemeral port for tests. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -359,6 +375,7 @@ export const layerTest: Layer.Layer< /** * Creates the Bun HTTP server and support-services layer from configurable serve options. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -613,6 +630,7 @@ class BunServerRequest extends Inspectable.Class implements ServerRequest.HttpSe )) return } + upgradedSources.add(this.source) const compressionThreshold = this.compressionThreshold resume(Effect.map(Deferred.await(deferred), (ws) => { const write = (chunk: Uint8Array | string | Socket.CloseEvent) => diff --git a/repos/effect/packages/platform/bun/src/BunHttpServerRequest.ts b/repos/effect/packages/platform/bun/src/BunHttpServerRequest.ts index 6c2b5a167..c6a0f5b00 100644 --- a/repos/effect/packages/platform/bun/src/BunHttpServerRequest.ts +++ b/repos/effect/packages/platform/bun/src/BunHttpServerRequest.ts @@ -5,6 +5,7 @@ * `Bun.BunRequest` stored inside a Bun-backed `HttpServerRequest`. It is meant * for code that needs to interoperate with Bun-specific request APIs. * + * @stability unstable * @since 4.0.0 */ import type { HttpServerRequest } from "effect/http/HttpServerRequest" @@ -12,6 +13,7 @@ import type { HttpServerRequest } from "effect/http/HttpServerRequest" /** * Returns the underlying `Bun.BunRequest` from an Effect `HttpServerRequest`. * + * @stability unstable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunMultipart.ts b/repos/effect/packages/platform/bun/src/BunMultipart.ts index 85ab4981d..b807a0fcf 100644 --- a/repos/effect/packages/platform/bun/src/BunMultipart.ts +++ b/repos/effect/packages/platform/bun/src/BunMultipart.ts @@ -6,6 +6,7 @@ * `persisted` collects the form and writes file parts to scoped temporary files * through the current `FileSystem`, `Path`, and `Scope` services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -19,6 +20,7 @@ import * as BunStream from "./BunStream.ts" /** * Parses a Bun `Request` body as multipart data and returns a stream of multipart parts. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -39,6 +41,7 @@ export const stream = (source: Request): Stream.Stream = NodePath.layer /** * Layer that provides the POSIX `Path` service for Bun using the shared Node path implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -31,6 +34,7 @@ export const layerPosix: Layer.Layer = NodePath.layerPosix /** * Layer that provides the Win32 `Path` service for Bun using the shared Node path implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunRuntime.ts b/repos/effect/packages/platform/bun/src/BunRuntime.ts index b353f8c73..dd6886968 100644 --- a/repos/effect/packages/platform/bun/src/BunRuntime.ts +++ b/repos/effect/packages/platform/bun/src/BunRuntime.ts @@ -5,6 +5,7 @@ * fiber in Bun. It reuses the shared Node runtime runner, including its error * reporting, signal handling, and optional teardown behavior. * + * @stability unstable * @since 4.0.0 */ import * as NodeRuntime from "@effect/platform-node-shared/NodeRuntime" @@ -32,6 +33,7 @@ import type { Teardown } from "effect/Runtime" * - `disablePrettyLogger`: Avoid adding the pretty logger. * - `teardown`: Provide custom finalization logic. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunServices.ts b/repos/effect/packages/platform/bun/src/BunServices.ts index b4447c0de..01dd9cb64 100644 --- a/repos/effect/packages/platform/bun/src/BunServices.ts +++ b/repos/effect/packages/platform/bun/src/BunServices.ts @@ -6,6 +6,7 @@ * and terminal services. Use the layer when a Bun program wants the standard * platform services from one place. * + * @stability unstable * @since 4.0.0 */ import type { Crypto } from "effect/Crypto" @@ -26,6 +27,7 @@ import * as BunTerminal from "./BunTerminal.ts" * The union of core services provided by the Bun platform layer, including child * process spawning, filesystem, path, stdio, and terminal services. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -35,6 +37,7 @@ export type BunServices = ChildProcessSpawner | Crypto | FileSystem | Path | Ter * Provides the default Bun implementations for child process spawning, * filesystem, path, stdio, and terminal services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunSink.ts b/repos/effect/packages/platform/bun/src/BunSink.ts index 6dbe5d87d..3771b2af5 100644 --- a/repos/effect/packages/platform/bun/src/BunSink.ts +++ b/repos/effect/packages/platform/bun/src/BunSink.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunSocket.ts b/repos/effect/packages/platform/bun/src/BunSocket.ts index 6a309ff88..7abdd2af7 100644 --- a/repos/effect/packages/platform/bun/src/BunSocket.ts +++ b/repos/effect/packages/platform/bun/src/BunSocket.ts @@ -6,6 +6,7 @@ * It also provides Bun WebSocket layers using `globalThis.WebSocket`, including * a constructor layer and a `Socket.Socket` layer for a WebSocket URL. * + * @stability unstable * @since 4.0.0 */ import type * as Duration from "effect/Duration" @@ -23,6 +24,7 @@ export * from "@effect/platform-node-shared/NodeSocket" * Provides a `Socket.WebSocketConstructor` backed by Bun's global * `WebSocket` implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -40,6 +42,7 @@ export const layerWebSocketConstructor: Layer.Layer< * `WebSocket` constructor, honoring protocol, open-timeout, and * high-water-mark options. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunSocketServer.ts b/repos/effect/packages/platform/bun/src/BunSocketServer.ts index 95e0a040f..97ed9e1c9 100644 --- a/repos/effect/packages/platform/bun/src/BunSocketServer.ts +++ b/repos/effect/packages/platform/bun/src/BunSocketServer.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunStdio.ts b/repos/effect/packages/platform/bun/src/BunStdio.ts index baed79ea3..f62dc6494 100644 --- a/repos/effect/packages/platform/bun/src/BunStdio.ts +++ b/repos/effect/packages/platform/bun/src/BunStdio.ts @@ -6,6 +6,7 @@ * is read from `process.stdin`, and output and error output write to * `process.stdout` and `process.stderr`. * + * @stability unstable * @since 4.0.0 */ import * as NodeStdio from "@effect/platform-node-shared/NodeStdio" @@ -16,6 +17,7 @@ import type { Stdio } from "effect/Stdio" * Provides the `Stdio` service backed by the current process arguments, * stdin, stdout, and stderr streams. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunStream.ts b/repos/effect/packages/platform/bun/src/BunStream.ts index 1629a5bc5..d68da2930 100644 --- a/repos/effect/packages/platform/bun/src/BunStream.ts +++ b/repos/effect/packages/platform/bun/src/BunStream.ts @@ -7,6 +7,7 @@ * `ReadableStream` adapter that uses Bun's `readMany` reader method to pull * batches of values into an Effect `Stream`. * + * @stability unstable * @since 4.0.0 */ import * as Arr from "effect/Array" @@ -27,6 +28,7 @@ export * from "@effect/platform-node-shared/NodeStream" * Creates a stream from a `ReadableStream` using Bun's optimized `.readMany` * API. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/bun/src/BunTerminal.ts b/repos/effect/packages/platform/bun/src/BunTerminal.ts index 88b3ebc8d..c3bea24bf 100644 --- a/repos/effect/packages/platform/bun/src/BunTerminal.ts +++ b/repos/effect/packages/platform/bun/src/BunTerminal.ts @@ -5,6 +5,7 @@ * creates a scoped process-backed `Terminal` service, and `layer` provides the * default terminal service with the standard quit behavior for key input. * + * @stability unstable * @since 4.0.0 */ import * as NodeTerminal from "@effect/platform-node-shared/NodeTerminal" @@ -17,6 +18,7 @@ import type { Terminal, UserInput } from "effect/Terminal" * Creates a scoped `Terminal` service backed by process stdin/stdout, using the * optional predicate to decide when key input should end the input stream. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -26,6 +28,7 @@ export const make: (shouldQuit?: (input: UserInput) => boolean) => Effect Effect.promise(() => fetch(url, { headers: { connection: "close" } }).then((response) => response.text())) -const readWebSocketClose = (port: number, opened: Deferred.Deferred) => +const readWebSocketClose = (port: number, opened: Deferred.Deferred, path = "/") => Effect.callback((resume) => { const socket = Net.createConnection({ host: "127.0.0.1", port }) let received = Buffer.alloc(0) @@ -32,7 +36,7 @@ const readWebSocketClose = (port: number, opened: Deferred.Deferred) => }) socket.on("connect", () => socket.write([ - "GET / HTTP/1.1", + `GET ${path} HTTP/1.1`, "Host: 127.0.0.1:" + port, "Connection: Upgrade", "Upgrade: websocket", @@ -47,6 +51,10 @@ const readWebSocketClose = (port: number, opened: Deferred.Deferred) => if (!upgraded) { const headerEnd = received.indexOf("\r\n\r\n") if (headerEnd === -1) return + if (!received.subarray(0, headerEnd).toString().startsWith("HTTP/1.1 101")) { + resume(Effect.fail(new Error("WebSocket upgrade was refused"))) + return + } upgraded = true received = received.subarray(headerEnd + 4) Effect.runSync(Deferred.succeed(opened, undefined)) @@ -59,6 +67,27 @@ const readWebSocketClose = (port: number, opened: Deferred.Deferred) => return Effect.sync(() => socket.destroy()) }) +// Force-stop Bun after the close frame; graceful stop can hang here. +const makeForceStoppableServer = Effect.gen(function*() { + const serve = Bun.serve + let forceStop: (() => void) | undefined + Bun.serve = ((options: Parameters[0]) => { + const bunServer = serve(options) + forceStop = () => { + bunServer.stop(true) + } + return bunServer + }) as typeof Bun.serve + const server = yield* BunHttpServer.make({ + hostname: "127.0.0.1", + port: 0, + gracefulShutdownTimeout: "100 millis" + }).pipe(Effect.ensuring(Effect.sync(() => { + Bun.serve = serve + }))) + return { server, forceStop: () => forceStop?.() } +}) + interface WebSocketFrame { readonly opcode: number readonly payload: Uint8Array @@ -412,46 +441,71 @@ describe("BunHttpServer", () => { it.effect(`closes a WebSocket with the handler's ${name} code`, () => Effect.gen(function*() { const opened = yield* Deferred.make() - // Force-stop Bun after the close frame; graceful stop can hang here. - const serve = Bun.serve - let forceStop: (() => void) | undefined - Bun.serve = ((options: Parameters[0]) => { - const bunServer = serve(options) - forceStop = () => { - bunServer.stop(true) - } - return bunServer - }) as typeof Bun.serve - const server = yield* BunHttpServer.make({ - hostname: "127.0.0.1", - port: 0, - gracefulShutdownTimeout: "100 millis" - }).pipe(Effect.ensuring(Effect.sync(() => { - Bun.serve = serve - }))) - yield* server.serve(Effect.gen(function*() { - const request = yield* HttpServerRequest.HttpServerRequest - const socket = yield* request.upgrade - const readerScope = yield* Scope.fork(yield* Effect.scope) - yield* socket.reader.pipe(Scope.provide(readerScope)) - yield* Deferred.await(opened) - if (exit === "explicit") { - const writer = yield* socket.writer - yield* writer.write(new Socket.CloseEvent(4400, "handler closed")) - } - if (exit === "interrupt") return yield* Effect.interrupt - if (exit === "failure" || exit === "explicit") return yield* Effect.fail(new Error("handler failed")) - if (exit === "defect") return yield* Effect.die(new Error("handler defect")) - return HttpServerResponse.empty() - })) - yield* Effect.addFinalizer(() => Effect.sync(() => forceStop?.())) + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/") Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + }) + const { forceStop, server } = yield* makeForceStoppableServer + yield* server.serve( + Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const socket = yield* request.upgrade + const readerScope = yield* Scope.fork(yield* Effect.scope) + yield* socket.reader.pipe(Scope.provide(readerScope)) + yield* Deferred.await(opened) + if (exit === "explicit") { + const writer = yield* socket.writer + yield* writer.write(new Socket.CloseEvent(4400, "handler closed")) + } + if (exit === "interrupt") return yield* Effect.interrupt + if (exit === "failure" || exit === "explicit") return yield* Effect.fail(new Error("handler failed")) + if (exit === "defect") return yield* Effect.die(new Error("handler defect")) + return HttpServerResponse.empty() + }), + HttpMiddleware.logger + ).pipe(Effect.provide(Logger.layer([logger]))) + yield* Effect.addFinalizer(() => Effect.sync(forceStop)) const port = (server.address as NetAddress.InetAddress).port const actual = yield* readWebSocketClose(port, opened) - forceStop?.() + forceStop() assert.strictEqual(actual, code) + if (exit === "success") assert.strictEqual(yield* Deferred.await(logged), 101) }).pipe(Effect.timeout("5 seconds")), 10000) } + it.effect("logs status 101 for a WebSocket upgraded through a prefixed route", () => + Effect.gen(function*() { + const opened = yield* Deferred.make() + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/ws") Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + }) + const app = yield* HttpRouter.toHttpEffect(HttpRouter.use((router) => + router.prefixed("/ws").add( + "GET", + "/", + Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const socket = yield* request.upgrade + const readerScope = yield* Scope.fork(yield* Effect.scope) + yield* socket.reader.pipe(Scope.provide(readerScope)) + yield* Deferred.await(opened) + return HttpServerResponse.empty() + }) + ) + )) + const { forceStop, server } = yield* makeForceStoppableServer + yield* server.serve(app, HttpMiddleware.logger).pipe(Effect.provide(Logger.layer([logger]))) + yield* Effect.addFinalizer(() => Effect.sync(forceStop)) + const port = (server.address as NetAddress.InetAddress).port + const actual = yield* readWebSocketClose(port, opened, "/ws") + forceStop() + assert.strictEqual(actual, 1000) + assert.strictEqual(yield* Deferred.await(logged), 101) + }).pipe(Effect.timeout("5 seconds")), 10000) + it.effect("fails a concurrent reader waiting behind a closed reader", () => Effect.gen(function*() { const secondReaderFailed = yield* Deferred.make() diff --git a/repos/effect/packages/platform/deno/CHANGELOG.md b/repos/effect/packages/platform/deno/CHANGELOG.md index 4e4741082..7939ce186 100644 --- a/repos/effect/packages/platform/deno/CHANGELOG.md +++ b/repos/effect/packages/platform/deno/CHANGELOG.md @@ -1,5 +1,16 @@ # @effect/platform-deno +## 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. + +- [#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. +- 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), [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519)]: + - effect@4.0.2 + - @effect/platform-node-shared@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/platform/deno/package.json b/repos/effect/packages/platform/deno/package.json index 1fe008bbc..ed2b368ea 100644 --- a/repos/effect/packages/platform/deno/package.json +++ b/repos/effect/packages/platform/deno/package.json @@ -1,7 +1,7 @@ { "name": "@effect/platform-deno", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Platform specific implementations for the Deno runtime", "homepage": "https://effect.website", @@ -76,7 +76,7 @@ }, "devDependencies": { "@std/assert": "jsr:^1.0.19", - "@testcontainers/redis": "^12.1.0", + "@testcontainers/redis": "^12.2.0", "@types/deno": "^2.7.0", "effect": "workspace:^" } diff --git a/repos/effect/packages/platform/deno/src/DenoChildProcessSpawner.ts b/repos/effect/packages/platform/deno/src/DenoChildProcessSpawner.ts index 049442547..2b415af90 100644 --- a/repos/effect/packages/platform/deno/src/DenoChildProcessSpawner.ts +++ b/repos/effect/packages/platform/deno/src/DenoChildProcessSpawner.ts @@ -5,6 +5,7 @@ * detached process groups, so killing a handle terminates only its direct child; * descendants spawned by that child are left running. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "effect/Array" @@ -393,6 +394,7 @@ const make = Effect.gen(function*() { /** * Layer that provides the Deno child process spawner. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -401,6 +403,7 @@ export const layer: Layer.Layer = Layer.e /** * Result of flattening a pipeline of commands. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -412,6 +415,7 @@ export interface FlattenedPipeline { /** * Flattens a command into standard commands and their pipe options. * + * @stability unstable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoClusterHttp.ts b/repos/effect/packages/platform/deno/src/DenoClusterHttp.ts index cb9708902..589f964be 100644 --- a/repos/effect/packages/platform/deno/src/DenoClusterHttp.ts +++ b/repos/effect/packages/platform/deno/src/DenoClusterHttp.ts @@ -6,6 +6,7 @@ * choosing serialization, runner health checks, runner storage, message * storage, and optional client-only mode from the supplied options. * + * @stability unstable * @since 4.0.0 */ import * as HttpRunner from "effect/cluster/HttpRunner" @@ -38,6 +39,7 @@ export { /** * Layer that provides a Kubernetes HTTP client for runner health checks. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -47,6 +49,7 @@ export { /** * Layer that provides a native Deno HTTP server for cluster runners. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -74,6 +77,7 @@ export const layerHttpServer: Layer.Layer< * Creates Deno cluster layers for HTTP or WebSocket transport, configuring * serialization, storage, runner health, and optional client-only mode. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoClusterSocket.ts b/repos/effect/packages/platform/deno/src/DenoClusterSocket.ts index 3dfa3ed94..a86f1befa 100644 --- a/repos/effect/packages/platform/deno/src/DenoClusterSocket.ts +++ b/repos/effect/packages/platform/deno/src/DenoClusterSocket.ts @@ -7,6 +7,7 @@ * Deno connections have no native idle-timeout option, so peer connections use * only the one-second open timeout. * + * @stability unstable * @since 4.0.0 */ import * as K8sHttpClient from "effect/cluster/K8sHttpClient" @@ -38,6 +39,7 @@ import * as DenoSocketServer from "./DenoSocketServer.ts" /** * Provides the cluster `RpcClientProtocol` using native Deno TCP sockets. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -69,6 +71,7 @@ export const layerClientProtocol: Layer.Layer< * Provides the socket server used by cluster runners, listening on * `ShardingConfig.runnerListenAddress` or `runnerAddress`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -92,6 +95,7 @@ export const layerSocketServer: Layer.Layer< * Creates Deno socket cluster layers, configuring serialization, storage, * runner health, and optional client-only mode. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -172,6 +176,7 @@ export const layer = < * Layer that provides `K8sHttpClient`, using a scoped native Deno HTTP client * with the Kubernetes service-account CA certificate when it is available. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoCrypto.ts b/repos/effect/packages/platform/deno/src/DenoCrypto.ts index 1a0d0dafd..9445355e7 100644 --- a/repos/effect/packages/platform/deno/src/DenoCrypto.ts +++ b/repos/effect/packages/platform/deno/src/DenoCrypto.ts @@ -3,6 +3,7 @@ * * This module uses Deno's global Web Crypto API. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -14,6 +15,7 @@ import * as PlatformError from "effect/PlatformError" /** * Provides the Web Crypto API used by the Crypto service implementation. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -24,6 +26,7 @@ export const WebCrypto = Context.Reference("@effect/platform-deno/Crypto /** * A layer that provides Effect's Crypto service using Deno's Web Crypto API. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoFileSystem.ts b/repos/effect/packages/platform/deno/src/DenoFileSystem.ts index 6029b642a..223a20690 100644 --- a/repos/effect/packages/platform/deno/src/DenoFileSystem.ts +++ b/repos/effect/packages/platform/deno/src/DenoFileSystem.ts @@ -1,6 +1,7 @@ /** * Deno implementation of Effect's `FileSystem` service. * + * @stability unstable * @since 4.0.0 */ import { copy as denoCopy, expandGlob, walk } from "@std/fs" @@ -510,6 +511,7 @@ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend), /** * Provides the `FileSystem` service backed by Deno filesystem APIs. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoHttpClient.ts b/repos/effect/packages/platform/deno/src/DenoHttpClient.ts index 23d51c8aa..742051bcd 100644 --- a/repos/effect/packages/platform/deno/src/DenoHttpClient.ts +++ b/repos/effect/packages/platform/deno/src/DenoHttpClient.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoHttpPlatform.ts b/repos/effect/packages/platform/deno/src/DenoHttpPlatform.ts index b98d6a2ba..d34294eb1 100644 --- a/repos/effect/packages/platform/deno/src/DenoHttpPlatform.ts +++ b/repos/effect/packages/platform/deno/src/DenoHttpPlatform.ts @@ -9,6 +9,7 @@ * The provided layer uses strong ETags, unlike the portable `HttpPlatform` * layer, which uses weak ETags. * + * @stability unstable * @since 4.0.0 */ import * as NodeHttpCompression from "@effect/platform-node-shared/NodeHttpCompression" @@ -36,6 +37,7 @@ const compression = NodeHttpCompression.make(Platform.makeCompressionWeb({ * Creates the Deno `HttpPlatform`, serving file responses from resource-backed * readable streams and adding content type and content length headers. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -102,6 +104,7 @@ export const make = Platform.make({ * Provides the Deno `HttpPlatform` together with its filesystem and strong ETag * services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoHttpServer.ts b/repos/effect/packages/platform/deno/src/DenoHttpServer.ts index e1b0c834e..3de399fbd 100644 --- a/repos/effect/packages/platform/deno/src/DenoHttpServer.ts +++ b/repos/effect/packages/platform/deno/src/DenoHttpServer.ts @@ -1,6 +1,7 @@ /** * Native Deno implementation of the Effect `HttpServer`. * + * @stability unstable * @since 4.0.0 */ import * as Config from "effect/Config" @@ -51,6 +52,7 @@ import * as DenoServices from "./DenoServices.ts" * WebSocket settings that apply to every upgrade; `protocol` therefore cannot * vary per request. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -61,6 +63,7 @@ export type ServeOptions = /** * Creates a scoped native Deno HTTP server. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -113,7 +116,9 @@ export const make = Effect.fnUntraced(function*( const httpEffect = HttpEffect.toHandled(httpApp, (request, response) => { const denoRequest = request as DenoServerRequest - if (denoRequest.upgraded) return cancelResponseBody(response.body) + if (upgradedSources.has(denoRequest.source)) { + return Effect.as(cancelResponseBody(response.body), upgradedResponse) + } return Effect.flatMap( makeResponse(request, response, services, scope), (response) => Effect.sync(() => denoRequest.resolve(response)) @@ -207,6 +212,7 @@ const makeResponse = Effect.fnUntraced(function*( /** * Provides only the native Deno HTTP server. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -224,6 +230,7 @@ export const layerServer: ( /** * Provides Deno HTTP platform services and the standard Deno service set. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -236,6 +243,7 @@ export const layerHttpServices: Layer.Layer void - public upgraded = false public headersOverride?: Headers.Headers | undefined private remoteAddressOverride?: Option.Option | undefined @@ -458,7 +467,7 @@ class DenoServerRequest extends Inspectable.Class implements ServerRequest.HttpS }), (upgrade) => { const ws = bufferedWebSocket(upgrade.socket) - this.upgraded = true + upgradedSources.add(this.source) this.resolve(upgrade.response) return Socket.fromWebSocket( Effect.acquireRelease( @@ -551,6 +560,12 @@ const bufferedWebSocket = (ws: WebSocket): Socket.WebSocketLike => { } } +// Reported to middleware in place of the handler's discarded response. +const upgradedResponse = ServerResponse.empty({ status: 101 }) + +// Keyed by source so request copies from `modify` share the upgrade state. +const upgradedSources = new WeakSet() + const cancelResponseBody = (body: HttpBody.HttpBody): Effect.Effect => { if (body._tag === "Raw" && typeof ReadableStream !== "undefined" && body.body instanceof ReadableStream) { const stream = body.body diff --git a/repos/effect/packages/platform/deno/src/DenoHttpServerRequest.ts b/repos/effect/packages/platform/deno/src/DenoHttpServerRequest.ts index 2a5854a56..f2ef8837e 100644 --- a/repos/effect/packages/platform/deno/src/DenoHttpServerRequest.ts +++ b/repos/effect/packages/platform/deno/src/DenoHttpServerRequest.ts @@ -5,6 +5,7 @@ * `Deno.serve` always receives a standard `Request`. Connection information is * intentionally kept internal to `DenoHttpServer`. * + * @stability unstable * @since 4.0.0 */ import type { HttpServerRequest } from "effect/http/HttpServerRequest" @@ -12,6 +13,7 @@ import type { HttpServerRequest } from "effect/http/HttpServerRequest" /** * Returns the underlying web-standard `Request` from an Effect `HttpServerRequest`. * + * @stability unstable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoKeyValueStore.ts b/repos/effect/packages/platform/deno/src/DenoKeyValueStore.ts index b7cbee707..43be6315b 100644 --- a/repos/effect/packages/platform/deno/src/DenoKeyValueStore.ts +++ b/repos/effect/packages/platform/deno/src/DenoKeyValueStore.ts @@ -1,6 +1,7 @@ /** * Deno-backed `KeyValueStore` layers using Web Storage APIs. * + * @stability unstable * @since 4.0.0 */ @@ -10,6 +11,7 @@ import * as KeyValueStore from "effect/persistence/KeyValueStore" /** * Creates a `KeyValueStore` layer backed by `localStorage`, with values stored between sessions. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -20,6 +22,7 @@ export const layerLocalStorage: Layer.Layer = KeyVa /** * Creates a `KeyValueStore` layer backed by `sessionStorage`, with values stored only for the current session. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoMultipart.ts b/repos/effect/packages/platform/deno/src/DenoMultipart.ts index 950e0f5f7..db6efb0ac 100644 --- a/repos/effect/packages/platform/deno/src/DenoMultipart.ts +++ b/repos/effect/packages/platform/deno/src/DenoMultipart.ts @@ -7,6 +7,7 @@ * temporary files through the current `FileSystem`, `Path`, and `Scope` * services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -19,6 +20,7 @@ import * as Stream from "effect/Stream" /** * Parses a web `Request` body as multipart data and returns a stream of multipart parts. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -39,6 +41,7 @@ export const stream = (source: Request): Stream.Stream ".txt" * ``` * + * @stability unstable * @since 4.0.0 */ @@ -55,6 +56,7 @@ const fileUrlOps = (impl: { /** * A {@linkplain Layer.Layer | layer} that provides POSIX path operations. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -70,6 +72,7 @@ export const layerPosix: Layer.Layer = Layer.succeed(Path.Path)( /** * A {@linkplain Layer.Layer | layer} that provides Windows path operations. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -87,6 +90,7 @@ export const layerWin32: Layer.Layer = Layer.succeed( /** * A {@linkplain Layer.Layer | layer} that provides OS-agnostic path operations. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoRuntime.ts b/repos/effect/packages/platform/deno/src/DenoRuntime.ts index 2b4355026..0ea56528b 100644 --- a/repos/effect/packages/platform/deno/src/DenoRuntime.ts +++ b/repos/effect/packages/platform/deno/src/DenoRuntime.ts @@ -1,6 +1,7 @@ /** * Deno helper for running a root Effect program. * + * @stability unstable * @since 4.0.0 */ @@ -10,6 +11,7 @@ import * as Runtime from "effect/Runtime" /** * Run an Effect as the entrypoint to a Deno application. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoServices.ts b/repos/effect/packages/platform/deno/src/DenoServices.ts index 1a6afe452..b41e1b369 100644 --- a/repos/effect/packages/platform/deno/src/DenoServices.ts +++ b/repos/effect/packages/platform/deno/src/DenoServices.ts @@ -6,6 +6,7 @@ * and terminal services. Use the layer when a Deno program wants the standard * platform services from one place. * + * @stability unstable * @since 4.0.0 */ import type { Crypto } from "effect/Crypto" @@ -26,6 +27,7 @@ import * as DenoTerminal from "./DenoTerminal.ts" * The union of core services provided by the Deno platform layer, including * child process spawning, crypto, filesystem, path, stdio, and terminal services. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -35,6 +37,7 @@ export type DenoServices = ChildProcessSpawner | Crypto | FileSystem | Path | Te * Provides the default Deno implementations for child process spawning, * crypto, filesystem, path, stdio, and terminal services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoSocket.ts b/repos/effect/packages/platform/deno/src/DenoSocket.ts index ecb1e6f05..15895bb0e 100644 --- a/repos/effect/packages/platform/deno/src/DenoSocket.ts +++ b/repos/effect/packages/platform/deno/src/DenoSocket.ts @@ -7,6 +7,7 @@ * `keepAlive` options have no effect on Unix connections. A `CloseEvent` always * closes gracefully because Deno has no equivalent of Node's reset-on-close. * + * @stability unstable * @since 4.0.0 */ import * as Arr from "effect/Array" @@ -24,6 +25,7 @@ import * as Socket from "effect/socket/Socket" /** * Options for opening a TCP or Unix connection. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -35,6 +37,7 @@ export type ConnectOptions = (Deno.ConnectOptions | Deno.UnixConnectOptions) & { /** * Options for opening a TCP or Unix connection. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -45,6 +48,7 @@ export type TcpOptions = ConnectOptions & { /** * Service tag for the underlying Deno connection. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -66,6 +70,7 @@ export class Conn extends Context.Service()( * `rejectUnauthorized` to `false` disables hostname verification only; * certificate-chain validation still applies. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -268,6 +273,7 @@ export const fromConn = ( * `Deno.connect` promise. The scope finalizer closes a connection that arrives * after the timeout. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -329,6 +335,7 @@ export const makeTcp = (options: TcpOptions): Effect.Effect => { /** * Creates a channel over a native Deno TCP or Unix connection. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -345,6 +352,7 @@ export const makeTcpChannel = ( /** * Provides a socket by opening a native Deno TCP or Unix connection. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -356,6 +364,7 @@ export const layerTcp: (options: ConnectOptions) => Layer.Layer< /** * Creates a socket layer connected to a URL with Deno's global WebSocket. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -371,6 +380,7 @@ export const layerWebSocket = (url: string, options?: { /** * Provides the WebSocket constructor backed by `globalThis.WebSocket`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoSocketServer.ts b/repos/effect/packages/platform/deno/src/DenoSocketServer.ts index 95e0a040f..97ed9e1c9 100644 --- a/repos/effect/packages/platform/deno/src/DenoSocketServer.ts +++ b/repos/effect/packages/platform/deno/src/DenoSocketServer.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoStdio.ts b/repos/effect/packages/platform/deno/src/DenoStdio.ts index 8a79a6050..9e4c24011 100644 --- a/repos/effect/packages/platform/deno/src/DenoStdio.ts +++ b/repos/effect/packages/platform/deno/src/DenoStdio.ts @@ -5,6 +5,7 @@ * arguments and Web Streams. Standard input remains open, and standard output * and error output are not closed unless requested through `endOnDone`. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -33,6 +34,7 @@ const output = ( * Provides the `Stdio` service backed by `Deno.args`, `Deno.stdin`, * `Deno.stdout`, and `Deno.stderr`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/DenoTerminal.ts b/repos/effect/packages/platform/deno/src/DenoTerminal.ts index cb02cf119..04cdbff02 100644 --- a/repos/effect/packages/platform/deno/src/DenoTerminal.ts +++ b/repos/effect/packages/platform/deno/src/DenoTerminal.ts @@ -5,6 +5,7 @@ * creates a scoped process-backed `Terminal` service, and `layer` provides the * default terminal service with the standard quit behavior for key input. * + * @stability unstable * @since 4.0.0 */ import * as NodeTerminal from "@effect/platform-node-shared/NodeTerminal" @@ -17,6 +18,7 @@ import type { Terminal, UserInput } from "effect/Terminal" * Creates a scoped `Terminal` service backed by process stdin/stdout, using the * optional predicate to decide when key input should end the input stream. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -26,6 +28,7 @@ export const make: (shouldQuit?: (input: UserInput) => boolean) => Effect = Layer.sync( /** * Layer that provides the worker runner platform using the supplied `MessagePort`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/deno/src/index.ts b/repos/effect/packages/platform/deno/src/index.ts index f9d41cf40..ba396297c 100644 --- a/repos/effect/packages/platform/deno/src/index.ts +++ b/repos/effect/packages/platform/deno/src/index.ts @@ -1,65 +1,78 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as DenoChildProcessSpawner from "./DenoChildProcessSpawner.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoClusterHttp from "./DenoClusterHttp.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoClusterSocket from "./DenoClusterSocket.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoCrypto from "./DenoCrypto.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoFileSystem from "./DenoFileSystem.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoHttpClient from "./DenoHttpClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoHttpPlatform from "./DenoHttpPlatform.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoHttpServer from "./DenoHttpServer.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoHttpServerRequest from "./DenoHttpServerRequest.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoKeyValueStore from "./DenoKeyValueStore.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoMultipart from "./DenoMultipart.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoPath from "./DenoPath.ts" @@ -71,41 +84,49 @@ export * as DenoPath from "./DenoPath.ts" export * as DenoRedis from "./DenoRedis.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoRuntime from "./DenoRuntime.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoServices from "./DenoServices.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoSocket from "./DenoSocket.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoSocketServer from "./DenoSocketServer.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoStdio from "./DenoStdio.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoTerminal from "./DenoTerminal.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoWorker from "./DenoWorker.ts" /** + * @stability unstable * @since 4.0.0 */ export * as DenoWorkerRunner from "./DenoWorkerRunner.ts" diff --git a/repos/effect/packages/platform/deno/test/DenoHttpServer.test.ts b/repos/effect/packages/platform/deno/test/DenoHttpServer.test.ts index 00f6ba5cc..e2dbe17a3 100644 --- a/repos/effect/packages/platform/deno/test/DenoHttpServer.test.ts +++ b/repos/effect/packages/platform/deno/test/DenoHttpServer.test.ts @@ -1,6 +1,7 @@ import * as DenoHttpServer from "@effect/platform-deno/DenoHttpServer" import { assert, describe, it } from "@effect/vitest" import * as ByteSize from "effect/ByteSize" +import * as Deferred from "effect/Deferred" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" import * as Fiber from "effect/Fiber" @@ -22,9 +23,11 @@ import * as Multipart from "effect/http/Multipart" import * as UrlParams from "effect/http/UrlParams" import * as Latch from "effect/Latch" import * as Layer from "effect/Layer" +import * as Logger from "effect/Logger" import * as ManagedRuntime from "effect/ManagedRuntime" import type * as NetAddress from "effect/net/NetAddress" import * as Queue from "effect/Queue" +import * as References from "effect/References" import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" import type * as Socket from "effect/socket/Socket" @@ -429,7 +432,7 @@ describe("DenoHttpServer", () => { Tracer.Tracer, Tracer.make({ span(options) { - assert.strictEqual(options.name, "http.client GET") + assert.strictEqual(options.name, "GET") assert.strictEqual(options.kind, "client") assert(options.parent._tag === "Some") if (options.parent.value._tag !== "Span") throw new Error("Expected span parent") @@ -715,11 +718,42 @@ describe("DenoHttpServer", () => { it.effect("round trips WebSocket frames and closes cleanly", () => Effect.gen(function*() { - yield* serveWebSocket(echoWebSocket) + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/") Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + }) + yield* serveWebSocket(echoWebSocket).pipe(Effect.provide(Logger.layer([logger]))) const server = yield* HttpServer.HttpServer const port = (server.address as NetAddress.InetAddress).port const messages = yield* connectWebSocket(`ws://127.0.0.1:${port}/`, (socket) => socket.send("hello"), 1) assert.deepStrictEqual(messages, ["hello"]) + assert.strictEqual(yield* Deferred.await(logged), 101) + }).pipe(Effect.provide(DenoHttpServer.layerTest))) + + it.effect("logs status 101 for a WebSocket upgraded through a prefixed route", () => + Effect.gen(function*() { + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/ws") Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + }) + yield* HttpRouter.use((router) => + router.prefixed("/ws").add( + "GET", + "/", + Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + yield* echoWebSocket(yield* request.upgrade) + return HttpServerResponse.empty() + }) + ) + ).pipe(HttpRouter.serve, Layer.provide(Logger.layer([logger])), Layer.build) + const server = yield* HttpServer.HttpServer + const port = (server.address as NetAddress.InetAddress).port + const messages = yield* connectWebSocket(`ws://127.0.0.1:${port}/ws`, (socket) => socket.send("hello"), 1) + assert.deepStrictEqual(messages, ["hello"]) + assert.strictEqual(yield* Deferred.await(logged), 101) }).pipe(Effect.provide(DenoHttpServer.layerTest))) it.effect("preserves eager WebSocket frames across an async boundary", () => diff --git a/repos/effect/packages/platform/node-shared/CHANGELOG.md b/repos/effect/packages/platform/node-shared/CHANGELOG.md index bc1e7838f..e7ded6a7f 100644 --- a/repos/effect/packages/platform/node-shared/CHANGELOG.md +++ b/repos/effect/packages/platform/node-shared/CHANGELOG.md @@ -1,5 +1,15 @@ # @effect/platform-node-shared +## 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. + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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/platform/node-shared/package.json b/repos/effect/packages/platform/node-shared/package.json index 0efd24fcb..78880e750 100644 --- a/repos/effect/packages/platform/node-shared/package.json +++ b/repos/effect/packages/platform/node-shared/package.json @@ -1,7 +1,7 @@ { "name": "@effect/platform-node-shared", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Unified interfaces for common platform-specific services", "homepage": "https://effect.website", @@ -68,13 +68,13 @@ "effect": "workspace:^" }, "devDependencies": { - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "effect": "workspace:^", "tar": "^7.5.22", "tinybench": "^6.2.0" }, "dependencies": { - "@types/ws": "^8.18.1", + "@types/ws": "^8.18.2", "ws": "^8.22.0" } } diff --git a/repos/effect/packages/platform/node-shared/src/NodeChildProcessSpawner.ts b/repos/effect/packages/platform/node-shared/src/NodeChildProcessSpawner.ts index 26143d53d..f15aa85d6 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeChildProcessSpawner.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeChildProcessSpawner.ts @@ -24,6 +24,7 @@ * POSIX cleanup targets a numeric process-group ID. If the group disappears * and its ID is reused before cleanup, an unrelated group may be signalled. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "effect/Array" @@ -698,6 +699,7 @@ const make = Effect.gen(function*() { /** * Layer that provides the `NodeChildProcessSpawner` implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -714,6 +716,7 @@ export const layer: Layer.Layer< /** * Result of flattening a pipeline of commands. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -726,6 +729,7 @@ export interface FlattenedPipeline { * Flattens a `Command` into an array of `StandardCommand`s along with pipe * options for each connection. * + * @stability unstable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeClusterSocket.ts b/repos/effect/packages/platform/node-shared/src/NodeClusterSocket.ts index 9108030e9..4daa6f863 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeClusterSocket.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeClusterSocket.ts @@ -6,6 +6,7 @@ * and wraps them in the current RPC serialization protocol. `layerSocketServer` * exposes the socket server that receives incoming runner RPC traffic. * + * @stability unstable * @since 4.0.0 */ import * as Runners from "effect/cluster/Runners" @@ -24,6 +25,7 @@ import * as NodeSocketServer from "./NodeSocketServer.ts" * Provides the cluster `RpcClientProtocol` by opening TCP sockets to runner * addresses and using the current RPC serialization service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -56,6 +58,7 @@ export const layerClientProtocol: Layer.Layer< * Provides the socket server used by cluster runners, listening on * `ShardingConfig.runnerListenAddress` or `runnerAddress`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeCrypto.ts b/repos/effect/packages/platform/node-shared/src/NodeCrypto.ts index 15796f5eb..2c88cd940 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeCrypto.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeCrypto.ts @@ -6,6 +6,7 @@ * `make` as the concrete service value and `layer` for providing it through * Effect context. * + * @stability unstable * @since 1.0.0 */ import * as EffectCrypto from "effect/Crypto" @@ -43,6 +44,7 @@ const digest: EffectCrypto.Crypto["digest"] = (algorithm, data) => /** * The default Node.js Crypto service implementation. * + * @stability unstable * @category constructors * @since 1.0.0 */ @@ -54,6 +56,7 @@ export const make: EffectCrypto.Crypto = EffectCrypto.make({ /** * Layer that provides the Node.js Crypto service implementation. * + * @stability unstable * @category layers * @since 1.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeFileSystem.ts b/repos/effect/packages/platform/node-shared/src/NodeFileSystem.ts index 19fabb023..45d2bbecc 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeFileSystem.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeFileSystem.ts @@ -7,6 +7,7 @@ * links, metadata, temporary files and directories, and file watching through * the shared `FileSystem` service. * + * @stability unstable * @since 4.0.0 */ import * as BI from "effect/BigInt" @@ -704,6 +705,7 @@ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend), * Provides the `FileSystem` service backed by Node filesystem APIs, including * file operations, directory operations, links, metadata, and file watching. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeHttpCompression.ts b/repos/effect/packages/platform/node-shared/src/NodeHttpCompression.ts index 84254504f..f4bf61c05 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeHttpCompression.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeHttpCompression.ts @@ -6,6 +6,7 @@ * `node:zlib` APIs, preserving an exact `Content-Length`. Streaming bodies go * through `node:zlib` transform streams that flush each input chunk. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -20,6 +21,7 @@ import * as Zlib from "node:zlib" * The compression algorithms supported by the runtime's `node:zlib`. `zstd` * requires Node.js 22.15 or newer. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -81,6 +83,7 @@ const compress = ( * the asynchronous `node:zlib` APIs, setting the exact `Content-Length` of the * compressed body. All other bodies are delegated to `fallback`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -104,6 +107,7 @@ export const make = (fallback: Platform.Compression): Platform.Compression => ({ * Creates a `node:zlib` compression transform stream that flushes each input * chunk, for streaming response bodies. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -137,6 +141,7 @@ export const compressTransform = ( * A Web `ReadableStream` version of `compressTransform`, for platforms that * stream response bodies as Web streams. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodePath.ts b/repos/effect/packages/platform/node-shared/src/NodePath.ts index 2a29c60f4..10208ec6c 100644 --- a/repos/effect/packages/platform/node-shared/src/NodePath.ts +++ b/repos/effect/packages/platform/node-shared/src/NodePath.ts @@ -6,6 +6,7 @@ * `layerWin32` provide fixed POSIX and Windows variants. All three layers also * include helpers for converting between file paths and file URLs. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -42,6 +43,7 @@ const fileUrlOps = (windows: boolean | undefined) => ({ * Provides the `Path` service using Node's POSIX path implementation plus * file URL conversion helpers. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -55,6 +57,7 @@ export const layerPosix: Layer.Layer = Layer.succeed(Path)({ * Provides the `Path` service using Node's Windows path implementation plus * file URL conversion helpers. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -68,6 +71,7 @@ export const layerWin32: Layer.Layer = Layer.succeed(Path)({ * Provides the default `Path` service using the host platform's Node path * implementation plus file URL conversion helpers. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeRuntime.ts b/repos/effect/packages/platform/node-shared/src/NodeRuntime.ts index 570b24af7..79854aee9 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeRuntime.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeRuntime.ts @@ -6,6 +6,7 @@ * fiber, interrupts that fiber on `SIGINT` or `SIGTERM`, and delegates final * exit-code handling to the configured teardown. * + * @stability unstable * @since 4.0.0 */ import type { Effect } from "effect/Effect" @@ -16,6 +17,7 @@ import * as Runtime from "effect/Runtime" * `SIGINT` or `SIGTERM` and invoking the configured teardown to determine the * process exit code. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeSink.ts b/repos/effect/packages/platform/node-shared/src/NodeSink.ts index 487bd1512..d69118bd2 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeSink.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeSink.ts @@ -7,6 +7,7 @@ * the supplied `onError` function, and can end the writable when the upstream * data is done. * + * @stability unstable * @since 4.0.0 */ import type { NonEmptyReadonlyArray } from "effect/Array" @@ -23,6 +24,7 @@ import type { Writable } from "node:stream" * backpressure, mapping writable errors with `onError`, and ending the stream * on completion unless `endOnDone` is `false`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -41,6 +43,7 @@ export const fromWritable = ( * Node writable stream, respecting backpressure and optionally ending the * writable when upstream is done. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -70,6 +73,7 @@ export const fromWritableChannel = ( * The loop waits for `drain` when needed, fails on writable errors, and ends * the writable on upstream completion unless `endOnDone` is `false`. * + * @stability unstable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeSocket.ts b/repos/effect/packages/platform/node-shared/src/NodeSocket.ts index 897f1de1f..92898f69a 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeSocket.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeSocket.ts @@ -6,6 +6,7 @@ * channels, or layers. It also exposes the `NetSocket` service tag for the * underlying Node socket and re-exports the `ws` package namespace. * + * @stability unstable * @since 4.0.0 */ import * as Arr from "effect/Array" @@ -38,6 +39,7 @@ export * as NodeWS from "ws" * Service tag for the underlying Node `net.Socket` associated with the current * socket connection. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -85,6 +87,7 @@ const closeSocket = (conn: Net.Socket, isOpen: boolean) => { * Supports `openTimeout` and closes or destroys the underlying socket when the * reader scope is finalized. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -142,6 +145,7 @@ export const makeNet = ( * the internal buffer is full. `writeAll` corks the stream around the batch. * Releasing the writer scope half-closes the stream (`end()`). * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -510,6 +514,7 @@ export const fromDuplex = ( * Creates a `Channel` over a TCP socket, reading arrays of `Uint8Array` * chunks and writing arrays of bytes, strings, or socket close events. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -530,6 +535,7 @@ export const makeNetChannel = ( * Provides a `Socket.Socket` by opening a TCP connection with the supplied * Node `net` connection options. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -554,6 +560,7 @@ export const layerNet: (options: Net.NetConnectOpts) => Layer.Layer< * certificate, fails with a `SocketOpenError`. Supports `openTimeout` and * destroys the underlying socket when the reader scope is finalized. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -598,6 +605,7 @@ export const makeTls = ( * Creates a `Channel` over a TLS socket, reading arrays of `Uint8Array` * chunks and writing arrays of bytes, strings, or socket close events. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -618,6 +626,7 @@ export const makeTlsChannel = ( * Provides a `Socket.Socket` by opening a TLS connection with the supplied * Node `tls` connection options. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeSocketServer.ts b/repos/effect/packages/platform/node-shared/src/NodeSocketServer.ts index 4b293bd2b..667d3aaf3 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeSocketServer.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeSocketServer.ts @@ -9,6 +9,7 @@ * and have access to the per-connection WebSocket and `IncomingMessage` * services. * + * @stability unstable * @since 4.0.0 */ import type { Cause } from "effect/Cause" @@ -50,6 +51,7 @@ const socketAddressFromNode = ( * Service tag for the Node `IncomingMessage` associated with the current * WebSocket server connection. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -63,6 +65,7 @@ export class IncomingMessage extends Context.Service< * listening with the supplied options, queues pending connections until `run` * is called, and closes the server when the scope ends. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -83,6 +86,7 @@ export const make = ( * Provides a TCP `SocketServer` by creating and managing a scoped Node * `net.Server` with the supplied server and listen options. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -105,6 +109,7 @@ export const layer: ( * also provided as `NodeSocket.NetSocket`. Connections that fail the handshake * are destroyed and the server keeps listening. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -131,6 +136,7 @@ export const makeTls = ( * Provides a TLS `SocketServer` by creating and managing a scoped Node * `tls.Server` with the supplied TLS and listen options. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeStdio.ts b/repos/effect/packages/platform/node-shared/src/NodeStdio.ts index cd05c6e3b..158342f4e 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeStdio.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeStdio.ts @@ -7,6 +7,7 @@ * `process.stdout` and `process.stderr`. Standard input remains open, and * standard output and error output are not ended unless requested. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -21,6 +22,7 @@ import { fromReadable } from "./NodeStream.ts" * and `process.stderr`; stdin remains open and stdout/stderr are not ended by * default. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeStream.ts b/repos/effect/packages/platform/node-shared/src/NodeStream.ts index 9bff3a84b..2dfd28313 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeStream.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeStream.ts @@ -7,6 +7,7 @@ * `Readable`, and collects readable payloads into strings, array buffers, or * `Uint8Array`s with optional byte limits. * + * @stability unstable * @since 4.0.0 */ import * as Arr from "effect/Array" @@ -32,6 +33,7 @@ import { pullIntoWritable } from "./NodeSink.ts" * an optional chunk size, mapping stream errors with `onError`, and destroying * the readable on completion unless `closeOnDone` is `false`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const fromReadable = (options: { * errors with `onError` and destroying the readable on completion unless * `closeOnDone` is `false`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -71,6 +74,7 @@ export const fromReadableChannel = (opti * backpressure while emitting chunks read from the duplex and optionally ending * the writable side when upstream completes. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -119,6 +123,7 @@ export const fromDuplex = (stream: Stream.Stream(stream: Stream.Stream( * `onError` on stream errors or when `maxBytes` is exceeded and destroying the * stream on interruption or failure. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -315,6 +325,7 @@ export const toArrayBuffer = ( * Consumes a Node readable stream into a `Uint8Array`, using the same error * mapping and `maxBytes` handling as `toArrayBuffer`. * + * @stability unstable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/NodeTerminal.ts b/repos/effect/packages/platform/node-shared/src/NodeTerminal.ts index 3cd23e66e..9e2d0ee54 100644 --- a/repos/effect/packages/platform/node-shared/src/NodeTerminal.ts +++ b/repos/effect/packages/platform/node-shared/src/NodeTerminal.ts @@ -7,6 +7,7 @@ * `make` manages readline and TTY raw mode in a scope, while `layer` provides * the default service that ends key input on Ctrl+C or Ctrl+D. * + * @stability unstable * @since 4.0.0 */ import type * as Cause from "effect/Cause" @@ -26,6 +27,7 @@ import * as readline from "node:readline" * TTY raw mode while in scope and using the supplied predicate to decide when * key input should end. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -168,6 +170,7 @@ export const make: ( * Provides the default process-backed `Terminal` service, ending key input on * Ctrl+C or Ctrl+D. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node-shared/src/index.ts b/repos/effect/packages/platform/node-shared/src/index.ts index 759e93b65..329fb19b3 100644 --- a/repos/effect/packages/platform/node-shared/src/index.ts +++ b/repos/effect/packages/platform/node-shared/src/index.ts @@ -1,70 +1,84 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as NodeChildProcessSpawner from "./NodeChildProcessSpawner.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeClusterSocket from "./NodeClusterSocket.ts" /** + * @stability unstable * @since 1.0.0 */ export * as NodeCrypto from "./NodeCrypto.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeFileSystem from "./NodeFileSystem.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpCompression from "./NodeHttpCompression.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodePath from "./NodePath.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeRuntime from "./NodeRuntime.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSink from "./NodeSink.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSocket from "./NodeSocket.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSocketServer from "./NodeSocketServer.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeStdio from "./NodeStdio.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeStream from "./NodeStream.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeTerminal from "./NodeTerminal.ts" diff --git a/repos/effect/packages/platform/node/CHANGELOG.md b/repos/effect/packages/platform/node/CHANGELOG.md index 2b5c58795..507978404 100644 --- a/repos/effect/packages/platform/node/CHANGELOG.md +++ b/repos/effect/packages/platform/node/CHANGELOG.md @@ -1,5 +1,18 @@ # @effect/platform-node +## 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. + +- [#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. + +- [#8846](https://github.com/Effect-TS/effect/pull/8846) [`48a373b`](https://github.com/Effect-TS/effect/commit/48a373b2b4fdacc7e65fd05e5260b7b1c2f09b6d) Thanks @tim-smart! - Fix keep-alive requests hanging during graceful HTTP server shutdown. +- 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), [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519)]: + - effect@4.0.2 + - @effect/platform-node-shared@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/platform/node/package.json b/repos/effect/packages/platform/node/package.json index 1403c7954..4c77779dc 100644 --- a/repos/effect/packages/platform/node/package.json +++ b/repos/effect/packages/platform/node/package.json @@ -1,7 +1,7 @@ { "name": "@effect/platform-node", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Platform specific implementations for the Node.js runtime", "homepage": "https://effect.website", @@ -74,11 +74,11 @@ "redis": ">=5.0.0 <7.0.0" }, "devDependencies": { - "@testcontainers/mysql": "^12.1.0", - "@testcontainers/postgresql": "^12.1.0", - "@testcontainers/redis": "^12.1.0", - "@types/node": "^26.6.3", + "@testcontainers/mysql": "^12.2.0", + "@testcontainers/postgresql": "^12.2.0", + "@testcontainers/redis": "^12.2.0", + "@types/node": "^26.6.4", "effect": "workspace:^", - "redis": "^6.2.1" + "redis": "^6.3.0" } } diff --git a/repos/effect/packages/platform/node/src/NodeChildProcessSpawner.ts b/repos/effect/packages/platform/node/src/NodeChildProcessSpawner.ts index 19e498e43..68832f7a4 100644 --- a/repos/effect/packages/platform/node/src/NodeChildProcessSpawner.ts +++ b/repos/effect/packages/platform/node/src/NodeChildProcessSpawner.ts @@ -1,6 +1,7 @@ /** * Node.js implementation of `ChildProcessSpawner`. * + * @stability unstable * @since 4.0.0 */ export * from "@effect/platform-node-shared/NodeChildProcessSpawner" diff --git a/repos/effect/packages/platform/node/src/NodeClusterHttp.ts b/repos/effect/packages/platform/node/src/NodeClusterHttp.ts index e0362f331..57a1dd7f3 100644 --- a/repos/effect/packages/platform/node/src/NodeClusterHttp.ts +++ b/repos/effect/packages/platform/node/src/NodeClusterHttp.ts @@ -8,6 +8,7 @@ * this module re-exports the Kubernetes HTTP client layer used by runner health * checks. * + * @stability unstable * @since 4.0.0 */ import * as HttpRunner from "effect/cluster/HttpRunner" @@ -41,6 +42,7 @@ export { /** * Provides the Kubernetes HTTP client layer used by Kubernetes runner health checks. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -52,6 +54,7 @@ export { * transport, RPC serialization, message storage, runner health checks, and * optional client-only mode. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -139,6 +142,7 @@ export const layer = < * Provides the HTTP server and Node HTTP services used by cluster runners, * listening on `ShardingConfig.runnerListenAddress` or `runnerAddress`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeClusterSocket.ts b/repos/effect/packages/platform/node/src/NodeClusterSocket.ts index c053bcd43..e01c0f3f2 100644 --- a/repos/effect/packages/platform/node/src/NodeClusterSocket.ts +++ b/repos/effect/packages/platform/node/src/NodeClusterSocket.ts @@ -8,6 +8,7 @@ * Kubernetes-aware Undici dispatcher and HTTP client layers for runner health * checks. * + * @stability unstable * @since 4.0.0 */ import { layerClientProtocol, layerSocketServer } from "@effect/platform-node-shared/NodeClusterSocket" @@ -37,6 +38,7 @@ export { * Provides the cluster `RpcClientProtocol` using the shared socket client * implementation. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -45,6 +47,7 @@ export { * Provides the socket server used by Node cluster runners through the shared * socket server implementation. * + * @stability unstable * @category re-exports * @since 4.0.0 */ @@ -56,6 +59,7 @@ export { * serialization, message storage, runner health checks, and optional * client-only mode. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -173,6 +177,7 @@ export const layerDispatcherK8s: Layer.Layer = Layer. * Provides a `K8sHttpClient` backed by the Undici HTTP client and the * Kubernetes-aware dispatcher. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeCrypto.ts b/repos/effect/packages/platform/node/src/NodeCrypto.ts index 7102ab481..883c35cbe 100644 --- a/repos/effect/packages/platform/node/src/NodeCrypto.ts +++ b/repos/effect/packages/platform/node/src/NodeCrypto.ts @@ -9,6 +9,7 @@ * implementation. Digest failures are reported as platform errors, and SHA-1 * remains available only for interoperability with existing protocols. * + * @stability unstable * @since 1.0.0 */ import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto" @@ -18,6 +19,7 @@ import type * as Layer from "effect/Layer" /** * Layer that provides the Node.js Crypto service implementation. * + * @stability unstable * @category layers * @since 1.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeFileSystem.ts b/repos/effect/packages/platform/node/src/NodeFileSystem.ts index b079872a7..2f9cd5357 100644 --- a/repos/effect/packages/platform/node/src/NodeFileSystem.ts +++ b/repos/effect/packages/platform/node/src/NodeFileSystem.ts @@ -6,6 +6,7 @@ * links, temporary paths, and path watching. Effects still call the service from * `effect/FileSystem`; this module only chooses the Node implementation. * + * @stability unstable * @since 4.0.0 */ import * as NodeFileSystem from "@effect/platform-node-shared/NodeFileSystem" @@ -15,6 +16,7 @@ import type * as Layer from "effect/Layer" /** * Provides the `FileSystem` service backed by Node filesystem APIs. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeHttpClient.ts b/repos/effect/packages/platform/node/src/NodeHttpClient.ts index 6d2746278..53938cb24 100644 --- a/repos/effect/packages/platform/node/src/NodeHttpClient.ts +++ b/repos/effect/packages/platform/node/src/NodeHttpClient.ts @@ -7,6 +7,7 @@ * services and request options, and defines a lower-level `node:http` / * `node:https` client with scoped HTTP agent layers. * + * @stability unstable * @since 4.0.0 */ import * as Context from "effect/Context" @@ -51,6 +52,7 @@ export { * Use to access or override the fetch implementation used by the Node * fetch-based HTTP client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -58,6 +60,7 @@ export { /** * Layer that provides the fetch-based HTTP client implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -69,6 +72,7 @@ export { * * Use to provide default fetch request options for Node HTTP requests. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -384,6 +388,7 @@ export const layerUndici: Layer.Layer = Layer.provide(layerUn * Service tag for the paired Node `http` and `https` agents used by the * node:http-backed HTTP client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -396,6 +401,7 @@ export class HttpAgent extends Context.Service La * Provides the `HttpAgent` service using default scoped Node `http` and * `https` agents. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -437,6 +445,7 @@ export const layerAgent: Layer.Layer = layerAgentOptions() * current `HttpAgent`, streaming request bodies, and wrapping Node responses * as `HttpClientResponse` values. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -656,6 +665,7 @@ class NodeHttpResponse extends NodeHttpIncomingMessage im * Provides a node:http-backed `HttpClient` using the current `HttpAgent` * service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -669,6 +679,7 @@ export const layerNodeHttpNoAgent: Layer.Layer< * Provides a node:http-backed `HttpClient` together with default scoped Node * `http` and `https` agents. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeHttpIncomingMessage.ts b/repos/effect/packages/platform/node/src/NodeHttpIncomingMessage.ts index a4260494d..8f714e554 100644 --- a/repos/effect/packages/platform/node/src/NodeHttpIncomingMessage.ts +++ b/repos/effect/packages/platform/node/src/NodeHttpIncomingMessage.ts @@ -9,6 +9,7 @@ * typed headers, remote address lookup, stream access, and text, JSON, * URL-encoded, and array-buffer body readers. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -35,6 +36,7 @@ import * as NodeStream from "./NodeStream.ts" * The adapter exposes headers, remote address, stream access, and cached body * decoders. Subclasses provide the error mapping for unknown Node errors. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeHttpPlatform.ts b/repos/effect/packages/platform/node/src/NodeHttpPlatform.ts index b200031a4..9a2de8c69 100644 --- a/repos/effect/packages/platform/node/src/NodeHttpPlatform.ts +++ b/repos/effect/packages/platform/node/src/NodeHttpPlatform.ts @@ -6,6 +6,7 @@ * supports byte ranges, converts Web `File` values to readable streams, and * fills in content type and content length headers when needed. * + * @stability unstable * @since 4.0.0 */ import * as NodeHttpCompression from "@effect/platform-node-shared/NodeHttpCompression" @@ -108,6 +109,7 @@ const compression = NodeHttpCompression.make({ * Creates the Node `HttpPlatform`, serving file responses from Node readable * streams and adding MIME type and content-length headers when needed. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -156,6 +158,7 @@ export const make = Platform.make({ * Provides the Node `HttpPlatform` together with the filesystem and ETag * services it needs for file responses. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeHttpServer.ts b/repos/effect/packages/platform/node/src/NodeHttpServer.ts index 1c4996f65..56d6b8291 100644 --- a/repos/effect/packages/platform/node/src/NodeHttpServer.ts +++ b/repos/effect/packages/platform/node/src/NodeHttpServer.ts @@ -10,6 +10,7 @@ * constructors plus layers for the server alone, HTTP support services, the * combined server, configurable options, and tests. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -43,6 +44,7 @@ import { import * as Request from "effect/http/HttpServerRequest" import { HttpServerRequest } from "effect/http/HttpServerRequest" import type { HttpServerResponse } from "effect/http/HttpServerResponse" +import * as Response from "effect/http/HttpServerResponse" import type * as Multipart from "effect/http/Multipart" import * as Latch from "effect/Latch" import * as Layer from "effect/Layer" @@ -67,6 +69,7 @@ import { NodeWS } from "./NodeSocket.ts" /** * Options accepted by the Node `HttpServer` constructors and layers. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -91,6 +94,7 @@ export interface Options extends Net.ListenOptions { * with the supplied options, registers request and upgrade handling, and closes * the server during scope finalization with optional graceful-shutdown control. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -166,11 +170,14 @@ export const make = Effect.fnUntraced(function*( middleware: middleware as any, scope }) - yield* Scope.addFinalizerExit(serveScope, () => { - server.off("request", handler) - server.off("upgrade", upgradeHandler) - return preemptiveShutdown - }) + yield* Scope.addFinalizerExit(serveScope, () => + Effect.ensuring( + preemptiveShutdown, + Effect.sync(() => { + server.off("request", handler) + server.off("upgrade", upgradeHandler) + }) + )) server.on("request", handler) server.on("upgrade", upgradeHandler) }) @@ -182,6 +189,7 @@ export const make = Effect.fnUntraced(function*( * injecting a `HttpServerRequest` and interrupting the request fiber if the * client closes the response before it finishes. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -253,12 +261,11 @@ export const makeUpgradeHandler = < socket: Duplex, head: Buffer ) { - let upgraded = false let nodeResponse_: Http.ServerResponse | undefined = undefined const nodeResponse = () => { if (nodeResponse_ === undefined) { nodeResponse_ = new Http.ServerResponse(nodeRequest) - if (upgraded || socket.destroyed) { + if (request.upgraded || socket.destroyed) { // End without assigning the socket so handleResponse skips HTTP writes. nodeResponse_.end() } else { @@ -289,18 +296,15 @@ export const makeUpgradeHandler = < socket.once("close", onClose) wss.handleUpgrade(nodeRequest, socket, head, (ws) => { socket.off("close", onClose) - upgraded = true + request.upgraded = true resume(Effect.succeed(ws)) }) }), (ws, exit) => Effect.sync(() => ws.close(closeCode(exit))) ) )) - const context = Context.add( - services, - HttpServerRequest, - new ServerRequestImpl(nodeRequest, nodeResponse, upgradeEffect) - ) + const request = new ServerRequestImpl(nodeRequest, nodeResponse, upgradeEffect) + const context = Context.add(services, HttpServerRequest, request) const fiber = Fiber.runIn(Effect.runForkWith(context as Context.Context)(handledApp), options.scope) socket.on("error", () => {}) socket.on("close", () => { @@ -316,6 +320,7 @@ class ServerRequestImpl extends NodeHttpIncomingMessage impleme readonly [Request.TypeId]: typeof Request.TypeId readonly response: Http.ServerResponse | LazyArg private upgradeEffect?: Effect.Effect | undefined + upgraded = false readonly url: string private headersOverride?: Headers.Headers | undefined @@ -437,6 +442,7 @@ class ServerRequestImpl extends NodeHttpIncomingMessage impleme * Provides an `HttpServer` by creating and managing a scoped Node * `http.Server` with the supplied listen and shutdown options. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -449,6 +455,7 @@ export const layerServer: ( * Provides the Node HTTP support services used by `NodeHttpServer`, including * the HTTP platform, ETag generator, and core Node platform services. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -464,6 +471,7 @@ export const layerHttpServices: Layer.Layer< * Provides a Node `HttpServer` together with the Node HTTP platform, ETag, and * core platform services required to serve requests. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -484,6 +492,7 @@ export const layer = ( * and core Node platform services, reading the listen and shutdown options from * a `Config` value. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -505,6 +514,7 @@ export const layerConfig = ( * Provides a test HTTP server listening on an ephemeral port together with a * Fetch-backed `HttpClient` configured for server integration tests. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -533,10 +543,16 @@ export const layerTest: Layer.Layer< const closeCode = (exit: Exit.Exit): number => Exit.isSuccess(exit) ? 1000 : Cause.hasInterruptsOnly(exit.cause) ? 1001 : 1011 +// Reported to middleware in place of the handler's discarded response. +const upgradedResponse = Response.empty({ status: 101 }) + const handleResponse = ( request: HttpServerRequest, response: HttpServerResponse -): Effect.Effect => { +): Effect.Effect => { + if ((request as ServerRequestImpl).upgraded) { + return Effect.succeed(upgradedResponse) + } const nodeResponse = (request as ServerRequestImpl).resolvedResponse if (nodeResponse.writableEnded) { return Effect.void diff --git a/repos/effect/packages/platform/node/src/NodeHttpServerRequest.ts b/repos/effect/packages/platform/node/src/NodeHttpServerRequest.ts index f047523d3..fc95ba100 100644 --- a/repos/effect/packages/platform/node/src/NodeHttpServerRequest.ts +++ b/repos/effect/packages/platform/node/src/NodeHttpServerRequest.ts @@ -5,6 +5,7 @@ * `toServerResponse` returns the underlying Node `http.ServerResponse`, * evaluating the stored response thunk when the response was created lazily. * + * @stability unstable * @since 4.0.0 */ import type { HttpServerRequest } from "effect/http/HttpServerRequest" @@ -14,6 +15,7 @@ import type * as Http from "node:http" * Returns the underlying Node `IncomingMessage` for a platform Node * `HttpServerRequest`. * + * @stability unstable * @category accessors * @since 4.0.0 */ @@ -24,6 +26,7 @@ export const toIncomingMessage = (self: HttpServerRequest): Http.IncomingMessage * `HttpServerRequest`, evaluating the stored response thunk when the response * was created lazily. * + * @stability unstable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeMultipart.ts b/repos/effect/packages/platform/node/src/NodeMultipart.ts index ab1f09b90..645bf1942 100644 --- a/repos/effect/packages/platform/node/src/NodeMultipart.ts +++ b/repos/effect/packages/platform/node/src/NodeMultipart.ts @@ -8,6 +8,7 @@ * returns the underlying Node readable stream for file parts produced by this * parser. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -30,6 +31,7 @@ import * as NodeStream from "./NodeStream.ts" * stream of `Multipart.Part` values, converting parser failures to * `MultipartError`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -56,6 +58,7 @@ export const stream = ( * Parses multipart data from a Node readable request body and persists file * parts using the current `FileSystem`, `Path`, and `Scope` services. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -77,6 +80,7 @@ export const persisted = ( * Returns the underlying Node readable stream for a multipart file produced by * the Node multipart parser. * + * @stability unstable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeMultipartParser.ts b/repos/effect/packages/platform/node/src/NodeMultipartParser.ts index e6773046d..49f1dfaef 100644 --- a/repos/effect/packages/platform/node/src/NodeMultipartParser.ts +++ b/repos/effect/packages/platform/node/src/NodeMultipartParser.ts @@ -1,6 +1,7 @@ /** * Node.js streams adapter for the low-level multipart parser. * + * @stability unstable * @since 4.0.0 */ // oxlint-disable typescript/no-unsafe-declaration-merging @@ -13,6 +14,7 @@ import { Duplex, Readable } from "node:stream" /** * A part emitted by the Node.js multipart parser. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -21,6 +23,7 @@ export type Part = Field | FileStream /** * A parsed multipart field. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -33,6 +36,7 @@ export interface Field { /** * A Node.js duplex stream that emits parsed multipart parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -60,6 +64,7 @@ export interface MultipartStream extends Duplex { /** * Configuration for the Node.js multipart parser. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -70,6 +75,7 @@ export type NodeConfig = Omit & { /** * A Node.js duplex stream that parses multipart input. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -151,6 +157,7 @@ export class MultipartStream extends Duplex { /** * Creates a Node.js multipart parser stream. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -159,6 +166,7 @@ export const make = (config: NodeConfig): MultipartStream => new MultipartStream /** * A readable stream containing a parsed multipart file. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodePath.ts b/repos/effect/packages/platform/node/src/NodePath.ts index 1cdb758f8..db8f2d489 100644 --- a/repos/effect/packages/platform/node/src/NodePath.ts +++ b/repos/effect/packages/platform/node/src/NodePath.ts @@ -6,6 +6,7 @@ * implementation. The provided path services include Node file URL conversion * behavior. * + * @stability unstable * @since 4.0.0 */ import * as NodePath from "@effect/platform-node-shared/NodePath" @@ -16,6 +17,7 @@ import type { Path } from "effect/Path" * Provides the default Node `Path` service using the platform's `node:path` * implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -25,6 +27,7 @@ export const layer: Layer.Layer = NodePath.layer * Provides the `Path` service using Node's POSIX path implementation, * regardless of the host platform. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -34,6 +37,7 @@ export const layerPosix: Layer.Layer = NodePath.layerPosix * Provides the `Path` service using Node's Windows path implementation, * regardless of the host platform. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeRuntime.ts b/repos/effect/packages/platform/node/src/NodeRuntime.ts index c4f161ee8..e7a26ef31 100644 --- a/repos/effect/packages/platform/node/src/NodeRuntime.ts +++ b/repos/effect/packages/platform/node/src/NodeRuntime.ts @@ -6,6 +6,7 @@ * error reporting, `SIGINT` / `SIGTERM` interruption, and optional teardown * behavior. * + * @stability unstable * @since 4.0.0 */ import * as NodeRuntime from "@effect/platform-node-shared/NodeRuntime" @@ -33,6 +34,7 @@ import type * as Runtime from "effect/Runtime" * - `disableErrorReporting`: Turn off automatic error logging. * - `teardown`: Provide custom finalization logic. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeServices.ts b/repos/effect/packages/platform/node/src/NodeServices.ts index 3f6c0d34c..fbe0d1d34 100644 --- a/repos/effect/packages/platform/node/src/NodeServices.ts +++ b/repos/effect/packages/platform/node/src/NodeServices.ts @@ -6,6 +6,7 @@ * and terminal services. Use the layer when a Node program wants the standard * platform services from one place. * + * @stability unstable * @since 4.0.0 */ import type { Crypto } from "effect/Crypto" @@ -26,6 +27,7 @@ import * as NodeTerminal from "./NodeTerminal.ts" * The union of core services provided by the Node platform layer, including * child process spawning, filesystem, path, stdio, and terminal services. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -35,6 +37,7 @@ export type NodeServices = ChildProcessSpawner | Crypto | FileSystem | Path | St * Provides the default Node implementations for child process spawning, * filesystem, path, stdio, and terminal services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeSink.ts b/repos/effect/packages/platform/node/src/NodeSink.ts index 6dbe5d87d..3771b2af5 100644 --- a/repos/effect/packages/platform/node/src/NodeSink.ts +++ b/repos/effect/packages/platform/node/src/NodeSink.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeSocket.ts b/repos/effect/packages/platform/node/src/NodeSocket.ts index 0ffd6f79e..ded4d7255 100644 --- a/repos/effect/packages/platform/node/src/NodeSocket.ts +++ b/repos/effect/packages/platform/node/src/NodeSocket.ts @@ -7,6 +7,7 @@ * present and falls back to `ws`, one that always uses `ws`, and one that * creates a `Socket.Socket` layer for a WebSocket URL. * + * @stability unstable * @since 4.0.0 */ import { NodeWS as WS } from "@effect/platform-node-shared/NodeSocket" @@ -28,6 +29,7 @@ const makeWebSocketWS: Socket.WebSocketConstructor["Service"] = (url, options) = * Provides a `Socket.WebSocketConstructor`, using `globalThis.WebSocket` when * available and falling back to the `ws` package otherwise. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -49,6 +51,7 @@ export const layerWebSocketConstructor: Layer.Layer< * Provides a `Socket.WebSocketConstructor` backed explicitly by the `ws` * package. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -61,6 +64,7 @@ export const layerWebSocketConstructorWS: Layer.Layer< * constructor layer, honoring protocol, open-timeout, and high-water-mark * options. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeSocketServer.ts b/repos/effect/packages/platform/node/src/NodeSocketServer.ts index 95e0a040f..97ed9e1c9 100644 --- a/repos/effect/packages/platform/node/src/NodeSocketServer.ts +++ b/repos/effect/packages/platform/node/src/NodeSocketServer.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeStdio.ts b/repos/effect/packages/platform/node/src/NodeStdio.ts index a971944b7..a1aeb466d 100644 --- a/repos/effect/packages/platform/node/src/NodeStdio.ts +++ b/repos/effect/packages/platform/node/src/NodeStdio.ts @@ -6,6 +6,7 @@ * from `process.argv`, consuming input from `process.stdin`, and writing output * streams to `process.stdout` and `process.stderr`. * + * @stability unstable * @since 4.0.0 */ import * as NodeStdio from "@effect/platform-node-shared/NodeStdio" @@ -16,6 +17,7 @@ import type { Stdio } from "effect/Stdio" * Provides the `Stdio` service backed by the current process arguments, * stdin, stdout, and stderr streams. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeStream.ts b/repos/effect/packages/platform/node/src/NodeStream.ts index c98e67d0c..e9ac46b1d 100644 --- a/repos/effect/packages/platform/node/src/NodeStream.ts +++ b/repos/effect/packages/platform/node/src/NodeStream.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeTerminal.ts b/repos/effect/packages/platform/node/src/NodeTerminal.ts index 3b8171bcc..6f24f795a 100644 --- a/repos/effect/packages/platform/node/src/NodeTerminal.ts +++ b/repos/effect/packages/platform/node/src/NodeTerminal.ts @@ -5,6 +5,7 @@ * scoped process-backed `Terminal` service, and `layer` provides the default * service with the standard quit behavior for key input. * + * @stability unstable * @since 4.0.0 */ import * as NodeTerminal from "@effect/platform-node-shared/NodeTerminal" @@ -17,6 +18,7 @@ import type { Terminal, UserInput } from "effect/Terminal" * Creates a scoped `Terminal` service backed by process stdin/stdout, using the * optional predicate to decide when key input should end the input stream. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -26,6 +28,7 @@ export const make: (shouldQuit?: (input: UserInput) => boolean) => Effect = Layer.succeed(W * Provides the Node `WorkerPlatform` together with a `Worker.Spawner` created * from the supplied worker or child-process spawning function. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/NodeWorkerRunner.ts b/repos/effect/packages/platform/node/src/NodeWorkerRunner.ts index cfd37dbba..9e14cce76 100644 --- a/repos/effect/packages/platform/node/src/NodeWorkerRunner.ts +++ b/repos/effect/packages/platform/node/src/NodeWorkerRunner.ts @@ -8,6 +8,7 @@ * `WorkerRunner`, sends replies over the same channel, and closes when the * parent sends the close message. * + * @stability unstable * @since 4.0.0 */ import * as Cause from "effect/Cause" @@ -26,6 +27,7 @@ import * as WorkerThreads from "node:worker_threads" * thread or child process, routing parent messages to the registered handler * and sending responses back through the parent channel. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/platform/node/src/index.ts b/repos/effect/packages/platform/node/src/index.ts index c6430a870..f1d73e790 100644 --- a/repos/effect/packages/platform/node/src/index.ts +++ b/repos/effect/packages/platform/node/src/index.ts @@ -1,70 +1,84 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel(Node*.ts): Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as NodeChildProcessSpawner from "./NodeChildProcessSpawner.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeClusterHttp from "./NodeClusterHttp.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeClusterSocket from "./NodeClusterSocket.ts" /** + * @stability unstable * @since 1.0.0 */ export * as NodeCrypto from "./NodeCrypto.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeFileSystem from "./NodeFileSystem.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpClient from "./NodeHttpClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpIncomingMessage from "./NodeHttpIncomingMessage.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpPlatform from "./NodeHttpPlatform.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpServer from "./NodeHttpServer.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeHttpServerRequest from "./NodeHttpServerRequest.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeMultipart from "./NodeMultipart.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeMultipartParser from "./NodeMultipartParser.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodePath from "./NodePath.ts" @@ -76,51 +90,61 @@ export * as NodePath from "./NodePath.ts" export * as NodeRedis from "./NodeRedis.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeRuntime from "./NodeRuntime.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeServices from "./NodeServices.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSink from "./NodeSink.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSocket from "./NodeSocket.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeSocketServer from "./NodeSocketServer.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeStdio from "./NodeStdio.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeStream from "./NodeStream.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeTerminal from "./NodeTerminal.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeWorker from "./NodeWorker.ts" /** + * @stability unstable * @since 4.0.0 */ export * as NodeWorkerRunner from "./NodeWorkerRunner.ts" diff --git a/repos/effect/packages/platform/node/test/NodeHttpServer.test.ts b/repos/effect/packages/platform/node/test/NodeHttpServer.test.ts index 5ff81a718..77eedc2c0 100644 --- a/repos/effect/packages/platform/node/test/NodeHttpServer.test.ts +++ b/repos/effect/packages/platform/node/test/NodeHttpServer.test.ts @@ -2,7 +2,7 @@ import { NodeHttpServer } from "@effect/platform-node" import { NodeWS } from "@effect/platform-node/NodeSocket" import { assert, describe, expect, it } from "@effect/vitest" -import { ByteSize, Effect, Option } from "effect" +import { ByteSize, Effect, Exit, Logger, Option, References } from "effect" import * as Deferred from "effect/Deferred" import * as Duration from "effect/Duration" import * as Fiber from "effect/Fiber" @@ -489,7 +489,7 @@ describe("HttpServer", () => { Tracer.Tracer, Tracer.make({ span(options) { - assert.strictEqual(options.name, "http.client GET") + assert.strictEqual(options.name, "GET") assert.strictEqual(options.kind, "client") assert(options.parent._tag === "Some") if (options.parent.value._tag !== "Span") { @@ -748,6 +748,76 @@ describe("HttpServer", () => { assert.strictEqual(writeCount, 1) })) + it.live("answers keep-alive requests while graceful shutdown drains an existing connection", () => { + const requestStarted = Latch.makeUnsafe() + const releaseResponse = Latch.makeUnsafe() + const firstResponse = Latch.makeUnsafe() + const secondResponse = Latch.makeUnsafe() + const server = Http.createServer() + server.keepAliveTimeout = 5000 + const runtime = ManagedRuntime.make( + HttpServer.serve(Effect.gen(function*() { + yield* requestStarted.open + yield* releaseResponse.await + return HttpServerResponse.text("ok") + })).pipe( + Layer.provide(NodeHttpServer.layer(() => server, { + host: "127.0.0.1", + port: 0, + gracefulShutdownTimeout: "10 seconds" + })) + ) + ) + let socket: Net.Socket | undefined + let responseData = "" + + return Effect.gen(function*() { + yield* Effect.promise(() => runtime.context()) + const connection = Net.connect(tcpPort(server), "127.0.0.1") + socket = connection + connection.on("data", (chunk) => { + responseData += chunk.toString() + if (responseData.includes("\r\n\r\nok")) firstResponse.openUnsafe() + if ((responseData.match(/HTTP\/1\.1 \d{3}/g) ?? []).length >= 2) secondResponse.openUnsafe() + }) + yield* Effect.callback((resume) => { + connection.once("connect", () => resume(Effect.void)) + connection.on("error", (error) => resume(Effect.fail(error))) + }).pipe(Effect.timeout("2 seconds")) + + connection.write("GET /first HTTP/1.1\r\nHost: localhost\r\n\r\n") + yield* requestStarted.await.pipe(Effect.timeout("2 seconds")) + const disposed = runtime.dispose() + // Wait for server.close() to stop listening, without letting the first request finish. + yield* Effect.callback((resume) => { + const timer = setInterval(() => { + if (!server.listening) resume(Effect.void) + }, 1) + return Effect.sync(() => clearInterval(timer)) + }).pipe(Effect.timeout("2 seconds")) + + releaseResponse.openUnsafe() + yield* firstResponse.await.pipe(Effect.timeout("2 seconds")) + assert.include(responseData, "HTTP/1.1 200") + connection.write("GET /second HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n") + const answered = yield* secondResponse.await.pipe(Effect.timeoutOption("1 second")) + assert.strictEqual( + answered._tag, + "Some", + "the second keep-alive request must receive a response before the shutdown timeout" + ) + yield* Effect.promise(() => disposed).pipe(Effect.timeout("2 seconds")) + }).pipe( + Effect.ensuring( + Effect.sync(() => { + releaseResponse.openUnsafe() + socket?.destroy() + server.closeAllConnections() + }).pipe(Effect.andThen(Effect.promise(() => runtime.dispose()))) + ) + ) + }) + it.live("disposes after a client aborts a handler awaiting an upstream request", () => { const upstreamStarted = Latch.makeUnsafe() const upstream = Http.createServer(() => { @@ -914,6 +984,97 @@ describe("HttpServer", () => { expect(root).toEqual("root") }).pipe(Effect.provide(NodeHttpServer.layerTest))) + it.effect("logs and traces status 101 after an upgraded WebSocket closes", () => + Effect.gen(function*() { + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/ws") { + Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + } + }) + const traced = yield* Deferred.make() + const tracer = Tracer.make({ + span(options) { + const span = new Tracer.NativeSpan(options) + if (options.kind === "server") { + span.end = (endTime, exit) => { + Tracer.NativeSpan.prototype.end.call(span, endTime, exit) + Deferred.doneUnsafe(traced, Effect.succeed(span.attributes.get("http.response.status_code"))) + } + } + return span + } + }) + yield* HttpRouter.add( + "GET", + "/ws", + Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const socket = yield* request.upgrade + yield* Stream.runDrain(Socket.toStream(socket)).pipe(Effect.ignore) + return HttpServerResponse.empty() + }) + ).pipe( + (layer) => HttpRouter.serve(layer, { disableListenLog: true }), + Layer.provide(Logger.layer([logger])), + Layer.provide(Layer.succeed(Tracer.Tracer)(tracer)), + Layer.build + ) + const server = yield* HttpServer.HttpServer + const port = (server.address as NetAddress.InetAddress).port + const handshakeStatus = yield* Effect.callback((resume) => { + const ws = new NodeWS.WebSocket(`ws://127.0.0.1:${port}/ws`) + let status: number | undefined + ws.on("upgrade", (response) => { + status = response.statusCode + }) + ws.on("open", () => ws.close(1000)) + ws.on("close", () => resume(Effect.succeed(status))) + ws.on("error", (error) => resume(Effect.fail(error))) + return Effect.sync(() => ws.close()) + }) + assert.strictEqual(handshakeStatus, 101) + assert.strictEqual(yield* Deferred.await(logged), 101) + assert.strictEqual(yield* Deferred.await(traced), 101) + }).pipe(Effect.timeout("5 seconds"), Effect.provide(layerTestWebsocket)), 10000) + + for (const failHandler of [false, true]) { + it.effect( + `logs the fallback status when an upgrade fails and the handler ${failHandler ? "fails" : "recovers"}`, + () => + Effect.gen(function*() { + const logged = yield* Deferred.make() + const logger = Logger.make((options) => { + const annotations = options.fiber.getRef(References.CurrentLogAnnotations) + if (annotations["http.url"] === "/ws") { + Deferred.doneUnsafe(logged, Effect.succeed(annotations["http.status"])) + } + }) + yield* HttpRouter.add( + "GET", + "/ws", + Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const result = yield* Effect.exit(request.upgrade) + assert.isTrue(Exit.isFailure(result)) + if (failHandler) return yield* Effect.fail(new Error("upgrade failed")) + return HttpServerResponse.empty({ status: 426 }) + }) + ).pipe( + (layer) => HttpRouter.serve(layer, { disableListenLog: true }), + Layer.provide(Logger.layer([logger])), + Layer.build + ) + // An ordinary HTTP request has no upgrade transport, so acquisition fails. + const response = yield* HttpClient.get("/ws") + assert.strictEqual(response.status, failHandler ? 500 : 426) + assert.strictEqual(yield* Deferred.await(logged), response.status) + }).pipe(Effect.timeout("5 seconds"), Effect.provide(layerTestWebsocket)), + 10000 + ) + } + it.effect("websocket options are forwarded to the WebSocketServer", () => Effect.gen(function*() { yield* HttpRouter.add( diff --git a/repos/effect/packages/platform/node/test/RpcServer.test.ts b/repos/effect/packages/platform/node/test/RpcServer.test.ts index 7cdbf00e8..072b8994d 100644 --- a/repos/effect/packages/platform/node/test/RpcServer.test.ts +++ b/repos/effect/packages/platform/node/test/RpcServer.test.ts @@ -10,6 +10,97 @@ import { e2eSuite, UsersClient } from "./fixtures/rpc-e2e.ts" import { RpcLayer, User } from "./fixtures/rpc-schemas.ts" describe("RpcServer", () => { + describe("request defect isolation over HTTP", () => { + const Ok = Rpc.make("Ok", { success: Schema.String }) + const serverGroup = RpcGroup.make( + Ok, + Rpc.make("Invalid", { payload: { value: Schema.String } }) + ) + const clientGroup = RpcGroup.make( + Ok, + Rpc.make("Missing"), + Rpc.make("MissingWithSchemas", { success: Schema.Number, error: Schema.String }), + Rpc.make("Invalid", { payload: { value: Schema.Number } }) + ) + const Server = HttpRouter.serve( + RpcServer.layer(serverGroup).pipe( + Layer.provide(serverGroup.toLayer({ + Ok: () => Effect.succeed("ok"), + Invalid: () => Effect.void + })), + Layer.provideMerge(RpcServer.layerProtocolHttp({ path: "/rpc" })) + ), + { disableListenLog: true, disableLogger: true } + ) + + // Fingerprinted payloads require both peers to share the schema, so a + // mismatch is reported as a fingerprint error instead of the original + // diagnostic. The defect still only fails its own request. + const fingerprintDiagnostic = "Expected matching layout fingerprint" + const cases = [ + { + name: "JSON", + serialization: RpcSerialization.layerJson, + invalidPayload: ["Expected string", "at [\"value\"]"] + }, + { + name: "SchemaBinary", + serialization: RpcSerialization.layerSchemaBinary(), + invalidPayload: ["Missing key", "at [\"value\"]"] + }, + { + name: "SchemaBinary with fingerprints", + serialization: RpcSerialization.layerSchemaBinary({ fingerprintPayloads: true }), + unknownTag: fingerprintDiagnostic, + invalidPayload: [fingerprintDiagnostic] + } + ] + + const assertDie = (exit: Exit.Exit, diagnostics: ReadonlyArray) => { + assert(Exit.isFailure(exit)) + assert.strictEqual(exit.cause.reasons.length, 1) + assert.strictEqual(exit.cause.reasons[0]._tag, "Die") + const diagnostic = String(Cause.squash(exit.cause)) + for (const expected of diagnostics) { + assert.include(diagnostic, expected) + } + } + + for (const { invalidPayload, name, serialization, unknownTag } of cases) { + const ClientProtocol = RpcClient.layerProtocolHttp({ + url: "", + transformClient: HttpClient.mapRequest(HttpClientRequest.appendUrl("/rpc")) + }).pipe( + Layer.provideMerge(Server), + Layer.provide([NodeHttpServer.layerTest, serialization]) + ) + + for (const tag of ["Missing", "MissingWithSchemas"] as const) { + it.effect(`${name}: unknown tag ${tag} fails only its request and the client remains reusable`, () => + Effect.gen(function*() { + const client = yield* RpcClient.make(clientGroup) + assert.strictEqual(yield* client.Ok(), "ok") + const missing = yield* Effect.exit(client[tag]()) + assert.strictEqual(yield* client.Ok(), "ok") + if (unknownTag === undefined) { + assert.deepStrictEqual(missing, Exit.die(`Unknown request tag: ${tag}`)) + } else { + assertDie(missing, [unknownTag]) + } + }).pipe(Effect.provide(ClientProtocol))) + } + + it.effect(`${name}: an invalid payload fails only its request and the client remains reusable`, () => + Effect.gen(function*() { + const client = yield* RpcClient.make(clientGroup) + assert.strictEqual(yield* client.Ok(), "ok") + const invalid = yield* Effect.exit(client.Invalid({ value: 42 })) + assert.strictEqual(yield* client.Ok(), "ok") + assertDie(invalid, invalidPayload) + }).pipe(Effect.provide(ClientProtocol))) + } + }) + // http ndjson const HttpProtocol = RpcServer.layerProtocolHttp({ path: "/rpc" }) const HttpNdjsonServer = HttpRouter.serve( diff --git a/repos/effect/packages/sql/clickhouse/CHANGELOG.md b/repos/effect/packages/sql/clickhouse/CHANGELOG.md index d6c40dc96..92bf56128 100644 --- a/repos/effect/packages/sql/clickhouse/CHANGELOG.md +++ b/repos/effect/packages/sql/clickhouse/CHANGELOG.md @@ -1,5 +1,14 @@ # @effect/sql-clickhouse +## 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), [`48a373b`](https://github.com/Effect-TS/effect/commit/48a373b2b4fdacc7e65fd05e5260b7b1c2f09b6d), [`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 + - @effect/platform-node@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/sql/clickhouse/package.json b/repos/effect/packages/sql/clickhouse/package.json index c6bd64205..476378ef3 100644 --- a/repos/effect/packages/sql/clickhouse/package.json +++ b/repos/effect/packages/sql/clickhouse/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-clickhouse", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A Clickhouse toolkit for Effect", diff --git a/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts b/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts index 299452008..2a5e003ba 100644 --- a/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts +++ b/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts @@ -7,6 +7,7 @@ * names, while `layer` runs the migrations during layer construction and * provides no services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -24,6 +25,7 @@ export * from "effect/sql/Migrator" * Runs SQL migrations for ClickHouse using the supplied migrator options and * returns the applied migration IDs and names. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -39,6 +41,7 @@ export const run: ( * Creates a layer that runs the configured ClickHouse migrations during layer * construction and provides no services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/clickhouse/src/index.ts b/repos/effect/packages/sql/clickhouse/src/index.ts index cc54c9a21..f1b672316 100644 --- a/repos/effect/packages/sql/clickhouse/src/index.ts +++ b/repos/effect/packages/sql/clickhouse/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,6 +12,7 @@ export * as ClickhouseClient from "./ClickhouseClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as ClickhouseMigrator from "./ClickhouseMigrator.ts" diff --git a/repos/effect/packages/sql/d1/CHANGELOG.md b/repos/effect/packages/sql/d1/CHANGELOG.md index 39a33dc7a..3012cdf6a 100644 --- a/repos/effect/packages/sql/d1/CHANGELOG.md +++ b/repos/effect/packages/sql/d1/CHANGELOG.md @@ -1,5 +1,22 @@ # @effect/sql-d1 +## 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. + +- [#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`. + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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/sql/d1/package.json b/repos/effect/packages/sql/d1/package.json index 10b50a59e..b0410845b 100644 --- a/repos/effect/packages/sql/d1/package.json +++ b/repos/effect/packages/sql/d1/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-d1", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A Cloudflare D1 integration for Effect", @@ -72,6 +72,6 @@ "effect": "workspace:^" }, "dependencies": { - "@cloudflare/workers-types": "^5.20260926.1" + "@cloudflare/workers-types": "^5.20261003.1" } } diff --git a/repos/effect/packages/sql/d1/src/D1Client.ts b/repos/effect/packages/sql/d1/src/D1Client.ts index 9ec64d46f..57189e240 100644 --- a/repos/effect/packages/sql/d1/src/D1Client.ts +++ b/repos/effect/packages/sql/d1/src/D1Client.ts @@ -29,6 +29,7 @@ import * as Stream from "effect/Stream" const ATTR_DB_SYSTEM_NAME = "db.system.name" const ATTR_DB_OPERATION_NAME = "db.operation.name" +const ATTR_DB_OPERATION_BATCH_SIZE = "db.operation.batch.size" const ATTR_DB_QUERY_TEXT = "db.query.text" const classifyError = (cause: unknown, message: string, operation: string) => @@ -133,6 +134,11 @@ interface StatementWithTransformRows extends Statement.Statement { readonly transformRows: TransformRows | undefined } +const batchSpanName = (spanAttributes: ReadonlyArray): string => { + const target = Statement.spanTarget(spanAttributes) + return target === undefined ? "BATCH" : `BATCH ${target}` +} + const makeBatch = (options: { readonly db: D1Database readonly prepareCache: Cache.Cache @@ -145,8 +151,9 @@ const makeBatch = (options: { if (statements.length === 0) { return Effect.succeed([] as unknown as BatchResults) } + const isBatch = statements.length > 1 return Effect.useSpan( - "sql.execute", + isBatch ? batchSpanName(options.spanAttributes) : Statement.spanName(options.spanAttributes), { kind: "client" }, (span) => Effect.withFiber(Effect.fnUntraced(function*(fiber) { @@ -168,7 +175,10 @@ const makeBatch = (options: { for (const [key, value] of options.spanAttributes) { span.attribute(key, value) } - span.attribute(ATTR_DB_OPERATION_NAME, "batch") + if (isBatch) { + span.attribute(ATTR_DB_OPERATION_NAME, "BATCH") + span.attribute(ATTR_DB_OPERATION_BATCH_SIZE, statements.length) + } span.attribute(ATTR_DB_QUERY_TEXT, queryTexts.join("; ")) // D1 batches execute on the binding directly and intentionally cannot participate in SqlClient transactions. diff --git a/repos/effect/packages/sql/d1/src/index.ts b/repos/effect/packages/sql/d1/src/index.ts index cf247344c..33a7eb120 100644 --- a/repos/effect/packages/sql/d1/src/index.ts +++ b/repos/effect/packages/sql/d1/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/d1/test/Client.test.ts b/repos/effect/packages/sql/d1/test/Client.test.ts index 34730a0fc..276f876bd 100644 --- a/repos/effect/packages/sql/d1/test/Client.test.ts +++ b/repos/effect/packages/sql/d1/test/Client.test.ts @@ -1,7 +1,7 @@ import type { D1Result } from "@cloudflare/workers-types" import { D1Client } from "@effect/sql-d1" import { assert, describe, it } from "@effect/vitest" -import { Cause, Effect } from "effect" +import { Cause, Effect, Tracer } from "effect" import * as Reactivity from "effect/reactivity/Reactivity" import { Statement } from "effect/sql" import { D1Miniflare } from "./utils.ts" @@ -207,6 +207,28 @@ describe("Client", () => { assert.equal(Cause.hasDies(res), true) }).pipe(Effect.provide(D1Miniflare.layerClient))) + it.effect("names batches with the configured target", () => + Effect.gen(function*() { + const db = { + prepare: () => ({ bind: () => ({}) }), + batch: async (statements: Array) => statements.map(() => ({ results: [] })) + } as any + const sql = yield* D1Client.make({ db, spanAttributes: { "db.namespace": "app" } }) + const spans: Array = [] + const tracer = Tracer.make({ + span: (options) => { + const span = new Tracer.NativeSpan(options) + spans.push(span) + return span + } + }) + yield* sql.batch([sql`SELECT 1`, sql`SELECT 2`]).pipe(Effect.withTracer(tracer)) + assert.deepStrictEqual(spans.map((span) => span.name), ["BATCH app"]) + }).pipe( + Effect.scoped, + Effect.provide(Reactivity.layer) + )) + it.effect("should defect when batching in a transaction", () => Effect.gen(function*() { const sql = yield* D1Client.D1Client diff --git a/repos/effect/packages/sql/libsql/CHANGELOG.md b/repos/effect/packages/sql/libsql/CHANGELOG.md index f1125b05f..34c56b628 100644 --- a/repos/effect/packages/sql/libsql/CHANGELOG.md +++ b/repos/effect/packages/sql/libsql/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-libsql +## 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/sql/libsql/package.json b/repos/effect/packages/sql/libsql/package.json index 2368086c5..b1ea178fa 100644 --- a/repos/effect/packages/sql/libsql/package.json +++ b/repos/effect/packages/sql/libsql/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-libsql", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A libSQL toolkit for Effect", @@ -64,7 +64,7 @@ }, "devDependencies": { "effect": "workspace:^", - "testcontainers": "^12.1.0" + "testcontainers": "^12.2.0" }, "peerDependencies": { "effect": "workspace:^" diff --git a/repos/effect/packages/sql/libsql/src/LibsqlClient.ts b/repos/effect/packages/sql/libsql/src/LibsqlClient.ts index b6852d604..5415d9fa0 100644 --- a/repos/effect/packages/sql/libsql/src/LibsqlClient.ts +++ b/repos/effect/packages/sql/libsql/src/LibsqlClient.ts @@ -7,6 +7,7 @@ * SQLite failures as `SqlError`s, and provides transaction support with * savepoints. Streaming queries are not implemented by this driver. * + * @stability unstable * @since 4.0.0 */ import * as Libsql from "@libsql/client" @@ -34,6 +35,7 @@ const classifyError = (cause: unknown, message: string, operation: string) => /** * Runtime type identifier used to mark `LibsqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -42,6 +44,7 @@ export const TypeId: TypeId = "~@effect/sql-libsql/LibsqlClient" /** * Type-level identifier used to mark `LibsqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -50,6 +53,7 @@ export type TypeId = "~@effect/sql-libsql/LibsqlClient" /** * libSQL-backed SQL client service, extending `SqlClient` with its runtime type marker and client configuration. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -65,6 +69,7 @@ export interface LibsqlClient extends Client.SqlClient { * * Use to access or provide a libSQL client through the Effect context. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -75,6 +80,7 @@ let clientIdCounter = 0 /** * Configuration for a libSQL client, either by supplying connection options or an existing live libSQL client. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -83,6 +89,7 @@ export type LibsqlClientConfig = LibsqlClientConfig.Full | LibsqlClientConfig.Li /** * Namespace containing the configuration variants for `LibsqlClient`. * + * @stability unstable * @since 4.0.0 */ export declare namespace LibsqlClientConfig { @@ -177,6 +184,7 @@ interface LibsqlConnection extends Connection { /** * Creates a scoped libSQL SQL client with transaction support. When given connection options it creates and closes the SDK client; when given `liveClient`, the caller retains ownership. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -355,6 +363,7 @@ export const make = ( /** * Creates a layer from a `Config`-wrapped libSQL client configuration, providing both `LibsqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -377,6 +386,7 @@ export const layerConfig: ( /** * Creates a layer from a concrete libSQL client configuration, providing both `LibsqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts b/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts index e32e167c5..62ced8c00 100644 --- a/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts +++ b/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts @@ -8,6 +8,7 @@ * while `layer` runs migrations during layer construction and provides no * services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -24,6 +25,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -38,6 +40,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/libsql/src/index.ts b/repos/effect/packages/sql/libsql/src/index.ts index f61e5c314..8f87a05f0 100644 --- a/repos/effect/packages/sql/libsql/src/index.ts +++ b/repos/effect/packages/sql/libsql/src/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as LibsqlClient from "./LibsqlClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as LibsqlMigrator from "./LibsqlMigrator.ts" diff --git a/repos/effect/packages/sql/mssql/CHANGELOG.md b/repos/effect/packages/sql/mssql/CHANGELOG.md index 0b6d1149c..85a37b8fc 100644 --- a/repos/effect/packages/sql/mssql/CHANGELOG.md +++ b/repos/effect/packages/sql/mssql/CHANGELOG.md @@ -1,5 +1,15 @@ # @effect/sql-mssql +## 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. + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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/sql/mssql/package.json b/repos/effect/packages/sql/mssql/package.json index ee057d98b..45d4f0777 100644 --- a/repos/effect/packages/sql/mssql/package.json +++ b/repos/effect/packages/sql/mssql/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-mssql", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A Microsoft SQL Server toolkit for Effect", @@ -61,13 +61,13 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@testcontainers/mssqlserver": "^12.1.0", + "@testcontainers/mssqlserver": "^12.2.0", "effect": "workspace:^" }, "peerDependencies": { "effect": "workspace:^" }, "dependencies": { - "tedious": "^20.0.0" + "tedious": "^20.3.3" } } diff --git a/repos/effect/packages/sql/mssql/src/MssqlClient.ts b/repos/effect/packages/sql/mssql/src/MssqlClient.ts index 8249e3ae6..ad62d8fb7 100644 --- a/repos/effect/packages/sql/mssql/src/MssqlClient.ts +++ b/repos/effect/packages/sql/mssql/src/MssqlClient.ts @@ -10,6 +10,7 @@ * direct or config-backed layers, and default parameter type mappings. * Streaming queries are not implemented by this driver. * + * @stability unstable * @since 4.0.0 */ import * as Config from "effect/Config" @@ -146,6 +147,7 @@ const classifyError = ( /** * Runtime type identifier used to mark `MssqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -154,6 +156,7 @@ export const TypeId: unique symbol = Symbol.for("@effect/sql-mssql/MssqlClient") /** * Type-level identifier used to mark `MssqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -162,6 +165,7 @@ export type TypeId = typeof TypeId /** * Microsoft SQL Server client service, extending `SqlClient` with typed parameter fragments and stored procedure calls. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -198,6 +202,7 @@ export interface MssqlClient extends Client.SqlClient { * Use to access or provide a Microsoft SQL Server client through the Effect * context. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -206,6 +211,7 @@ export const MssqlClient = Context.Service("@effect/sql-mssql/Mssql /** * Configuration for a Microsoft SQL Server client, including connection, authentication, pool, parameter type, span attribute, and query/result name transform options. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -271,6 +277,7 @@ let clientIdCounter = 0 /** * Creates a scoped Microsoft SQL Server client backed by a connection pool, with transaction and stored procedure support. Streaming queries are not implemented. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -666,6 +673,7 @@ export const make = ( /** * Creates a layer from a `Config`-wrapped SQL Server client configuration, providing both `MssqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -688,6 +696,7 @@ export const layerConfig: ( /** * Creates a layer from a concrete SQL Server client configuration, providing both `MssqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -704,6 +713,7 @@ export const layer = ( /** * Creates the SQL Server statement compiler, using `@1`-style placeholders, bracket-escaped identifiers, and SQL Server `OUTPUT INSERTED` returning clauses. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts b/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts index 21a1a81b6..8d83748b6 100644 --- a/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts +++ b/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts @@ -7,6 +7,7 @@ * and names, while `layer` runs migrations during layer construction and * provides no services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -23,6 +24,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -37,6 +39,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/mssql/src/index.ts b/repos/effect/packages/sql/mssql/src/index.ts index 6aff44856..f3b08bf04 100644 --- a/repos/effect/packages/sql/mssql/src/index.ts +++ b/repos/effect/packages/sql/mssql/src/index.ts @@ -1,10 +1,12 @@ /** + * @stability unstable * @since 4.0.0 */ export { /** * @stability unstable + * @category re-exports * @since 4.0.0 */ TYPES as MssqlTypes @@ -13,11 +15,13 @@ export { // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as MssqlClient from "./MssqlClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as MssqlMigrator from "./MssqlMigrator.ts" diff --git a/repos/effect/packages/sql/mysql2/CHANGELOG.md b/repos/effect/packages/sql/mysql2/CHANGELOG.md index d9403f57f..c3b887a5c 100644 --- a/repos/effect/packages/sql/mysql2/CHANGELOG.md +++ b/repos/effect/packages/sql/mysql2/CHANGELOG.md @@ -1,5 +1,15 @@ # @effect/sql-mysql2 +## 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. + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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/sql/mysql2/package.json b/repos/effect/packages/sql/mysql2/package.json index f0588b592..cd668ecb2 100644 --- a/repos/effect/packages/sql/mysql2/package.json +++ b/repos/effect/packages/sql/mysql2/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-mysql2", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A MySQL toolkit for Effect", @@ -61,13 +61,13 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@testcontainers/mysql": "^12.1.0", + "@testcontainers/mysql": "^12.2.0", "effect": "workspace:^" }, "peerDependencies": { "effect": "workspace:^" }, "dependencies": { - "mysql2": "^3.24.4" + "mysql2": "^3.24.5" } } diff --git a/repos/effect/packages/sql/mysql2/src/MysqlClient.ts b/repos/effect/packages/sql/mysql2/src/MysqlClient.ts index c5f751f50..397dd8f45 100644 --- a/repos/effect/packages/sql/mysql2/src/MysqlClient.ts +++ b/repos/effect/packages/sql/mysql2/src/MysqlClient.ts @@ -8,6 +8,7 @@ * mysql2 query streams. It also provides direct and config-backed layers plus a * MySQL statement compiler. * + * @stability unstable * @since 4.0.0 */ import * as Config from "effect/Config" @@ -137,6 +138,7 @@ const classifyError = ( /** * Runtime type identifier used to mark `MysqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -145,6 +147,7 @@ export const TypeId: TypeId = "~@effect/sql-mysql2/MysqlClient" /** * Type-level identifier used to mark `MysqlClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -153,6 +156,7 @@ export type TypeId = "~@effect/sql-mysql2/MysqlClient" /** * mysql2-backed SQL client service, extending `SqlClient` with its runtime type marker and client configuration. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -168,6 +172,7 @@ export interface MysqlClient extends Client.SqlClient { * * Use to access or provide a mysql2 client through the Effect context. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -176,6 +181,7 @@ export const MysqlClient = Context.Service("@effect/sql-mysql2/Mysq /** * Configuration for a mysql2 client, including connection URI or connection fields, pool options, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -216,6 +222,7 @@ export interface MysqlClientConfig { /** * Creates a scoped MySQL client backed by a managed mysql2 pool, verifying connectivity and supporting streaming queries through mysql2 query streams. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -425,6 +432,7 @@ export const make = ( /** * Creates a layer from a `Config`-wrapped MySQL client configuration, providing both `MysqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -445,6 +453,7 @@ export const layerConfig = ( /** * Creates a layer from a concrete MySQL client configuration, providing both `MysqlClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -461,6 +470,7 @@ export const layer = ( /** * Creates the MySQL statement compiler, using `?` placeholders and backtick-escaped identifiers. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts b/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts index 74d089c3f..1f1dd96f3 100644 --- a/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts +++ b/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts @@ -7,6 +7,7 @@ * and names, while `layer` runs migrations during layer construction and * provides no services. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -23,6 +24,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -90,6 +92,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/mysql2/src/index.ts b/repos/effect/packages/sql/mysql2/src/index.ts index a49cb4eca..e5efdfaa1 100644 --- a/repos/effect/packages/sql/mysql2/src/index.ts +++ b/repos/effect/packages/sql/mysql2/src/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as MysqlClient from "./MysqlClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as MysqlMigrator from "./MysqlMigrator.ts" diff --git a/repos/effect/packages/sql/pg/CHANGELOG.md b/repos/effect/packages/sql/pg/CHANGELOG.md index 81510231e..fe0aaba3d 100644 --- a/repos/effect/packages/sql/pg/CHANGELOG.md +++ b/repos/effect/packages/sql/pg/CHANGELOG.md @@ -1,5 +1,19 @@ # @effect/sql-pg +## 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. + +- [#8861](https://github.com/Effect-TS/effect/pull/8861) [`7ccfeb3`](https://github.com/Effect-TS/effect/commit/7ccfeb35690fd245f5afbcc09da4df927eaf9a5e) Thanks @WolfieLeader! - Preserve stale prepared-statement errors (`0A000` or `26000`) in aborted transactions instead of masking them with a retry error (`25P02`). + +- [#8746](https://github.com/Effect-TS/effect/pull/8746) [`13eb009`](https://github.com/Effect-TS/effect/commit/13eb0099aa49495957b96beaf88ea95edcac9886) Thanks @tim-smart! - Fix binary PostgreSQL interval decoding. Interval values preserve months, days, and bigint microseconds independently, with support for interval arrays and typed interval parameters. + +- [#8854](https://github.com/Effect-TS/effect/pull/8854) [`2131d44`](https://github.com/Effect-TS/effect/commit/2131d44bd105766994201d3ffa3e180c0d33f655) Thanks @MatCer! - Release socket listeners and pool retirement hooks on PostgreSQL connection scope closure to avoid retaining closed connections and their pools. +- 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/sql/pg/package.json b/repos/effect/packages/sql/pg/package.json index b7eaaed4d..2b4c7e623 100644 --- a/repos/effect/packages/sql/pg/package.json +++ b/repos/effect/packages/sql/pg/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-pg", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A PostgreSQL toolkit for Effect", @@ -63,8 +63,8 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@testcontainers/postgresql": "^12.1.0", - "@types/node": "^26.6.3", + "@testcontainers/postgresql": "^12.2.0", + "@types/node": "^26.6.4", "effect": "workspace:^", "postgres": "^3.4.9", "tinybench": "^6.2.0" diff --git a/repos/effect/packages/sql/pg/src/PgAuth.ts b/repos/effect/packages/sql/pg/src/PgAuth.ts index a97e3a13a..6faa70a44 100644 --- a/repos/effect/packages/sql/pg/src/PgAuth.ts +++ b/repos/effect/packages/sql/pg/src/PgAuth.ts @@ -13,6 +13,7 @@ * are not supported. Server iteration counts above 1,000,000 are rejected * before password derivation. * + * @stability unstable * @since 4.0.0 */ import * as Data from "effect/Data" @@ -23,6 +24,7 @@ import { createHash, createHmac, pbkdf2Sync } from "node:crypto" /** * Failure returned when an authentication exchange cannot be completed. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -105,6 +107,7 @@ const md5PasswordUnsafe = (options: { /** * Computes the legacy PostgreSQL MD5 password response. * + * @stability unstable * @category authentication * @since 4.0.0 */ @@ -117,6 +120,7 @@ export const md5Password = (options: { /** * The only SASL mechanism this module implements. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -125,6 +129,7 @@ export const SCRAM_SHA_256 = "SCRAM-SHA-256" /** * State after the client's first message, awaiting the server's challenge. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -138,6 +143,7 @@ export interface ScramFirst { /** * State after the client's final message, awaiting the server signature. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -149,6 +155,7 @@ export interface ScramFinal { /** * The SCRAM exchange state. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -210,6 +217,7 @@ const scramInitUnsafe = (options: { /** * Creates the first SCRAM-SHA-256 client message. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -277,6 +285,7 @@ const scramContinueUnsafe = ( /** * Processes the server's SCRAM challenge and creates the client proof. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ @@ -309,6 +318,7 @@ const scramFinishUnsafe = (state: ScramFinal, challenge: Uint8Array): void => { /** * Verifies the server's final SCRAM message. * + * @stability unstable * @category SCRAM * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/PgClient.ts b/repos/effect/packages/sql/pg/src/PgClient.ts index 458df7fb4..6d3a0c63a 100644 --- a/repos/effect/packages/sql/pg/src/PgClient.ts +++ b/repos/effect/packages/sql/pg/src/PgClient.ts @@ -1,6 +1,7 @@ /** * PostgreSQL support for Effect SQL, backed by the native wire protocol client. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "effect/Array" @@ -29,6 +30,7 @@ import * as PgTypes from "./PgTypes.ts" /** * The runtime type identifier for `PgClient`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -37,6 +39,7 @@ export const TypeId: TypeId = "~@effect/sql-pg/PgClient" /** * The type-level identifier for `PgClient`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -45,6 +48,7 @@ export type TypeId = "~@effect/sql-pg/PgClient" /** * A PostgreSQL `SqlClient` with JSON and `LISTEN`/`NOTIFY` helpers. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -68,6 +72,7 @@ export interface PgClient extends Client.SqlClient { /** * The service tag for `PgClient`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -76,6 +81,7 @@ export const PgClient = Context.Service("@effect/sql-pg/PgClient") /** * Connection and query settings for a PostgreSQL client. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -149,6 +155,7 @@ export interface PgClientConfig { /** * PostgreSQL client settings with connection pool limits and timeouts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -163,6 +170,7 @@ export interface PgPoolConfig extends PgClientConfig { /** * Creates a scoped PostgreSQL client backed by a connection pool. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -179,6 +187,7 @@ export const make = (options: PgPoolConfig): Effect.Effect( /** * Creates a client layer from wrapped pool configuration. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -385,6 +396,7 @@ export const layerConfig = ( /** * Creates a client layer from pool configuration. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -395,6 +407,7 @@ export const layer = ( /** * Creates the PostgreSQL statement compiler. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -445,6 +458,7 @@ const escape = Statement.defaultEscape("\"") /** * PostgreSQL-specific statement fragments. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/PgConnection.ts b/repos/effect/packages/sql/pg/src/PgConnection.ts index 1b01a1d07..8ae5831a6 100644 --- a/repos/effect/packages/sql/pg/src/PgConnection.ts +++ b/repos/effect/packages/sql/pg/src/PgConnection.ts @@ -6,6 +6,7 @@ * Sessions support queries, streaming, `LISTEN`/`NOTIFY`, cancellation, and * exclusive ownership for transactions. * + * @stability unstable * @since 4.0.0 */ import type * as Arr from "effect/Array" @@ -39,6 +40,7 @@ import * as PgTypes from "./PgTypes.ts" /** * The runtime type identifier for `PgConnection`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const TypeId: TypeId = "~@effect/sql-pg/PgConnection" /** * The type-level identifier for `PgConnection`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -77,6 +80,7 @@ export type TypeId = "~@effect/sql-pg/PgConnection" * unpinned multiplexed connection cannot be interrupted because cancellation * could affect another fiber's query. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -135,6 +139,7 @@ const preparedCacheFor = (config: Config): PreparedCache | undefined => { /** * An object result row keyed by column name. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -145,6 +150,7 @@ export interface Row { /** * Metadata for one result column. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -156,6 +162,7 @@ export interface Field { /** * The result of a query. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -170,6 +177,7 @@ export interface Result { /** * A `NOTIFY` message received while listening on a channel. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -182,6 +190,7 @@ export interface Notification { /** * A connected and authenticated PostgreSQL session. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -253,6 +262,7 @@ export interface PgConnection { /** * The service tag for `PgConnection`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -271,6 +281,7 @@ export const PgConnection = Context.Service("@effect/sql-pg/PgConn * Use `sslmode=require` or explicit `ssl: true` to require encryption. * Unix sockets and custom streams should set `ssl.servername` explicitly. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -499,6 +510,11 @@ class PgConnectionImpl implements PgConnection { }), destroySocket ) + const socket = this.session.socket + socket.off("data", this.onData) + socket.off("error", this.onSocketError) + socket.off("close", this.onSocketClose) + this.retireHooks.clear() } /** Plans one execution. `cache` is `undefined` to force the unnamed path. */ @@ -1368,7 +1384,14 @@ class QueryMachine implements Consumer { return } if (this.phase === "error") { - if (message._tag === "ReadyForQuery") return this.complete(Effect.fail(this.failure!)) + if (message._tag === "ReadyForQuery") { + // Retrying in an aborted transaction would mask the original error with 25P02. + if (this.plan.stale && !this.plan.parses && message.status === "E") { + this.conn.prepared?.evict(this.plan.prepared!) + this.plan.stale = false + } + return this.complete(Effect.fail(this.failure!)) + } return this.failDesync(`Unexpected ${message._tag} after ErrorResponse`) } switch (message._tag) { diff --git a/repos/effect/packages/sql/pg/src/PgMigrator.ts b/repos/effect/packages/sql/pg/src/PgMigrator.ts index e10ce9a27..e54de4bba 100644 --- a/repos/effect/packages/sql/pg/src/PgMigrator.ts +++ b/repos/effect/packages/sql/pg/src/PgMigrator.ts @@ -7,6 +7,7 @@ * dumps are requested, it uses `pg_dump` and the usual process and filesystem * services. * + * @stability unstable * @since 4.0.0 */ import * as Effect from "effect/Effect" @@ -29,6 +30,7 @@ export * from "effect/sql/Migrator" /** * Runs PostgreSQL SQL migrations using the configured clients. Schema dumps use `pg_dump` and require child process, filesystem, and path services. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -102,6 +104,7 @@ export const run: ( /** * Creates a layer that runs PostgreSQL migrations during layer construction, including `pg_dump`-based schema dump support when requested. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/PgPool.ts b/repos/effect/packages/sql/pg/src/PgPool.ts index c3d75942c..9a933d392 100644 --- a/repos/effect/packages/sql/pg/src/PgPool.ts +++ b/repos/effect/packages/sql/pg/src/PgPool.ts @@ -1,6 +1,7 @@ /** * Pools of native `PgConnection` sessions. * + * @stability unstable * @since 4.0.0 */ import * as Clock from "effect/Clock" @@ -18,6 +19,7 @@ const defaultMultiplexConcurrency = 32 /** * The runtime type identifier for `PgPool`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -26,6 +28,7 @@ export const TypeId: TypeId = "~@effect/sql-pg/PgPool" /** * The type-level identifier for `PgPool`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -44,6 +47,7 @@ export type TypeId = "~@effect/sql-pg/PgPool" * queries. Reserved connections remain exclusive. Without multiplexing, every * checkout is exclusive. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -64,6 +68,7 @@ export interface Config extends PgConnection.Config { /** * A PostgreSQL session pool. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -104,6 +109,7 @@ export interface PgPool { /** * The service tag for `PgPool`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -118,6 +124,7 @@ export const PgPool = Context.Service("@effect/sql-pg/PgPool") * `minConnections` after `idleTimeout` without use. Closing the scope shuts * the pool down and releases every session. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/PgProtocol.ts b/repos/effect/packages/sql/pg/src/PgProtocol.ts index 2cf25b42c..a1db1fed0 100644 --- a/repos/effect/packages/sql/pg/src/PgProtocol.ts +++ b/repos/effect/packages/sql/pg/src/PgProtocol.ts @@ -15,6 +15,7 @@ * are held, but holding one keeps its whole pool buffer alive, so copy * anything that has to outlive the message it came from. * + * @stability unstable * @since 4.0.0 */ import * as Data from "effect/Data" @@ -23,6 +24,7 @@ import * as Result from "effect/Result" /** * Default `maxMessageSize` for `makeParser`: 16 MiB. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -34,6 +36,7 @@ const maxBufferSize = 64 * 1024 /** * An incremental decoder for the post-startup backend message stream. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -76,6 +79,7 @@ export interface Parser { * Special pre-startup replies have no type byte. Use `decodeSslResponse` for * those replies. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -182,6 +186,7 @@ export const makeParser = (options?: { /** * Prepares a named or unnamed statement. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -196,6 +201,7 @@ export interface Parse { * Binds parameter values to a prepared statement and creates a portal. * Parameters and results use the binary format. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -209,6 +215,7 @@ export interface Bind { /** * Runs a portal, optionally limiting the number of rows returned. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -221,6 +228,7 @@ export interface Execute { /** * Which kind of object a `Describe` or `Close` message names. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -229,6 +237,7 @@ export type DescribeTarget = "statement" | "portal" /** * Asks for the parameter and row shape of a statement or portal. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -241,6 +250,7 @@ export interface Describe { /** * Drops a prepared statement or portal. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -253,6 +263,7 @@ export interface Close { /** * Closes the current transaction block and requests a `ReadyForQuery`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -263,6 +274,7 @@ export interface Sync { /** * Asks the backend to deliver buffered output without ending the transaction. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -273,6 +285,7 @@ export interface Flush { /** * Ends the session. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -283,6 +296,7 @@ export interface Terminate { /** * Answers a cleartext or MD5 password request. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -294,6 +308,7 @@ export interface PasswordMessage { /** * Selects a SASL mechanism and carries its opaque initial response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -306,6 +321,7 @@ export interface SASLInitialResponse { /** * Carries an opaque SASL continuation payload. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -317,6 +333,7 @@ export interface SASLResponse { /** * Any message the client sends after startup. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -336,6 +353,7 @@ export type FrontendMessage = /** * Error produced when bytes cannot be interpreted as a protocol message. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -346,6 +364,7 @@ export class ParseError extends Data.TaggedError("PgProtocolParseError")<{ /** * Error returned when a frontend message cannot be encoded. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -731,6 +750,7 @@ const encodeParseUnsafe = (options: Omit): Uint8Array => { /** * Encodes a `Parse` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -804,6 +824,7 @@ const encodeBindUnsafe = (options: Omit): Uint8Array => { /** * Encodes a `Bind` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -815,6 +836,7 @@ export const encodeBind = (options: Omit): Result.Result( /** * Encodes an `Execute` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -954,6 +978,7 @@ export const encodeExecute = (options: Omit): Uint8Array => { /** * Encodes a `Describe` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -967,6 +992,7 @@ export const encodeDescribe = (options: Omit): Uint8Array => { /** * Encodes a `Close` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -980,6 +1006,7 @@ export const encodeClose = (options: Omit): Uint8Array => { /** * Encodes a `Sync` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -988,6 +1015,7 @@ export const encodeSync = (): Uint8Array => empty(0x53) /** * Encodes a `Flush` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -996,6 +1024,7 @@ export const encodeFlush = (): Uint8Array => empty(0x48) /** * Encodes a `Terminate` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1005,6 +1034,7 @@ export const encodeTerminate = (): Uint8Array => empty(0x58) * Encodes a `PasswordMessage`. The password is sent verbatim, so MD5 hashing * belongs to the caller - see `PgAuth.md5Password`. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1017,6 +1047,7 @@ export const encodePasswordMessage = (options: Omit): U /** * Encodes a `SASLInitialResponse` message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1035,6 +1066,7 @@ export const encodeSASLInitialResponse = (options: Omit): Uint8Ar /** * Encodes any frontend message. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1089,6 +1122,7 @@ const CANCEL_REQUEST_CODE = 80877102 * Startup parameters. `user` is required; any other run-time parameter the * server accepts may be passed alongside it. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1103,6 +1137,7 @@ export interface StartupParameters { * Encodes an `SSLRequest`. It has no type byte and is only valid before * startup. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1117,6 +1152,7 @@ export const encodeSslRequest = (): Uint8Array => { * Decodes the single byte the server sends in reply to an `SSLRequest`. `"S"` * means the server will speak TLS, `"N"` means it will not. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -1131,6 +1167,7 @@ export const decodeSslResponse = (byte: number): Result.Result<"S" | "N", ParseE * Encodes a `StartupMessage` for protocol 3.0. It has no type byte. * `client_encoding` defaults to `UTF8` because this codec always writes UTF-8. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1157,6 +1194,7 @@ export const encodeStartupMessage = (parameters: StartupParameters): Uint8Array * Encodes a `CancelRequest`. It has no type byte and is sent on a separate * connection, using the `pid` and `secret` from `BackendKeyData`. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1180,6 +1218,7 @@ export const encodeCancelRequest = (options: { /** * Authentication succeeded. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1190,6 +1229,7 @@ export interface AuthenticationOk { /** * The server wants the password in the clear. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1200,6 +1240,7 @@ export interface AuthenticationCleartextPassword { /** * The server wants an MD5-hashed password, salted with these four bytes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1211,6 +1252,7 @@ export interface AuthenticationMD5Password { /** * The server offers these SASL mechanisms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1222,6 +1264,7 @@ export interface AuthenticationSASL { /** * An opaque SASL challenge. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1233,6 +1276,7 @@ export interface AuthenticationSASLContinue { /** * The opaque final SASL payload, carrying the server signature. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1245,6 +1289,7 @@ export interface AuthenticationSASLFinal { * An authentication request this codec does not model, such as GSSAPI or * SSPI. The `method` is the raw sub-type integer. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1257,6 +1302,7 @@ export interface AuthenticationUnsupported { /** * Reports a run-time parameter value, at startup or whenever it changes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1269,6 +1315,7 @@ export interface ParameterStatus { /** * The identity a `CancelRequest` needs. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1282,6 +1329,7 @@ export interface BackendKeyData { * Transaction status: idle, in a transaction block, or in a failed * transaction block. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1290,6 +1338,7 @@ export type TransactionStatus = "I" | "T" | "E" /** * The backend is ready for a new query cycle. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1301,6 +1350,7 @@ export interface ReadyForQuery { /** * One column of a `RowDescription`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1317,6 +1367,7 @@ export interface FieldDescription { /** * Describes the columns a portal will return. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1329,6 +1380,7 @@ export interface RowDescription { * One result row. Values stay raw bytes; `null` is SQL NULL. Decoding them * requires the OIDs from the matching `RowDescription`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1346,6 +1398,7 @@ export interface DataRow { * from `offset` through `offset + size` belong to the field. A thrown error * permanently fails the parser. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1354,6 +1407,7 @@ export type FieldReader = (bytes: Uint8Array, offset: number, size: number, c /** * A command finished, reporting its tag such as `SELECT 3`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1365,6 +1419,7 @@ export interface CommandComplete { /** * The query string was empty. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1375,6 +1430,7 @@ export interface EmptyQueryResponse { /** * The statement or portal returns no rows. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1385,6 +1441,7 @@ export interface NoData { /** * A `Parse` succeeded. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1395,6 +1452,7 @@ export interface ParseComplete { /** * A `Bind` succeeded. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1405,6 +1463,7 @@ export interface BindComplete { /** * A `Close` succeeded. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1415,6 +1474,7 @@ export interface CloseComplete { /** * An `Execute` stopped at its row limit; the portal can be executed again. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1425,6 +1485,7 @@ export interface PortalSuspended { /** * The parameter OIDs of a described statement. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1437,6 +1498,7 @@ export interface ParameterDescription { * The fields of an `ErrorResponse` or `NoticeResponse`. Unrecognised field * codes are kept under their raw single-character key. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1465,6 +1527,7 @@ export interface ErrorFields { /** * An error. `code` is the SQLSTATE. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1476,6 +1539,7 @@ export interface ErrorResponse { /** * A warning or notice. Same field set as `ErrorResponse`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1487,6 +1551,7 @@ export interface NoticeResponse { /** * A `LISTEN`/`NOTIFY` message. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1501,6 +1566,7 @@ export interface NotificationResponse { * The server speaks an older minor protocol version, or did not recognise * some startup options. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1513,6 +1579,7 @@ export interface NegotiateProtocolVersion { /** * The server is ready to receive `COPY` data. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1525,6 +1592,7 @@ export interface CopyInResponse { /** * The server is about to send `COPY` data. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1537,6 +1605,7 @@ export interface CopyOutResponse { /** * The connection entered bidirectional `COPY` mode, as used by replication. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1549,6 +1618,7 @@ export interface CopyBothResponse { /** * A chunk of `COPY` data. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1560,6 +1630,7 @@ export interface CopyData { /** * The `COPY` stream ended. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1571,6 +1642,7 @@ export interface CopyDone { * A message whose type byte this codec does not know. The payload excludes * the type byte and the length prefix. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1583,6 +1655,7 @@ export interface Unknown { /** * Any message the server sends after startup. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/PgTypes.ts b/repos/effect/packages/sql/pg/src/PgTypes.ts index 66fd8a47b..51c99fdda 100644 --- a/repos/effect/packages/sql/pg/src/PgTypes.ts +++ b/repos/effect/packages/sql/pg/src/PgTypes.ts @@ -26,6 +26,11 @@ * preserve its UTC fields. `timestamptz` round trips preserve the instant * regardless of session timezone. * + * `interval` values decode to `Interval`, preserving calendar months, days, + * and signed bigint microseconds independently. No units are normalized. + * Convert microseconds explicitly before JSON serialization. + * + * @stability unstable * @since 4.0.0 */ import * as Data from "effect/Data" @@ -39,6 +44,7 @@ import type { ValueSink } from "./PgProtocol.ts" /** * Failure returned when a value cannot be encoded or decoded for its OID. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -288,6 +294,7 @@ const TZDISP_LIMIT_SECONDS = 57_600 /** * Type OIDs implemented by version 1 of this codec. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -311,6 +318,7 @@ export const OID = { time: 1083, timestamp: 1114, timestamptz: 1184, + interval: 1186, timetz: 1266, numeric: 1700, regclass: 2205, @@ -335,6 +343,7 @@ export const OID = { timeArray: 1183, timestampArray: 1115, timestamptzArray: 1185, + intervalArray: 1187, timetzArray: 1270, numericArray: 1231, regclassArray: 2210, @@ -362,6 +371,7 @@ const arrayToElement = new Map([ [OID.timeArray, OID.time], [OID.timestampArray, OID.timestamp], [OID.timestamptzArray, OID.timestamptz], + [OID.intervalArray, OID.interval], [OID.timetzArray, OID.timetz], [OID.numericArray, OID.numeric], [OID.regclassArray, OID.regclass], @@ -376,6 +386,7 @@ const elementToArray = new Map( /** * Options for registering a codec. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -391,6 +402,7 @@ export interface RegisterOptions { * A client-specific set of PostgreSQL binary codecs. Each registry starts with * the built-in codecs and does not affect the module-level registry. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -402,6 +414,7 @@ export interface Registry { * Returns the array OID whose elements have the given OID, or `undefined` * when there is no array type registered for it. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -885,6 +898,7 @@ const decodeUuid = (bytes: Uint8Array, offset: number, size: number): string => /** * A binary codec for a single OID. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -979,9 +993,9 @@ const utf8Codec: UnsafeCodec = codecOf( (sink, value) => sink.utf8(requireString(value, "text")) ) -const int8Value = (value: unknown): bigint => { - const big = requireBigInt(value, "int8") - if (big < INT64_MIN || big > INT64_MAX) fail(`int8 out of range: ${big}`) +const requireInt64 = (value: unknown, name: string): bigint => { + const big = requireBigInt(value, name) + if (big < INT64_MIN || big > INT64_MAX) fail(`${name} out of range: ${big}`) return big } @@ -1180,6 +1194,56 @@ const oidCodec: UnsafeCodec = codecOf( (sink, value) => sink.int32(requireInteger(value, "oid", 0, 4294967295)) ) +/** + * A PostgreSQL interval with independent signed calendar and time components. + * Months and days are int32 values; microseconds is an int64 value. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Interval { + readonly months: number + readonly days: number + readonly microseconds: bigint +} + +const intervalValue = (value: unknown): Interval => { + if (typeof value !== "object" || value === null) return fail("Expected an object for interval") + const interval = value as Interval + const months = requireInteger(interval.months, "interval months", INT32_MIN, INT32_MAX) + const days = requireInteger(interval.days, "interval days", INT32_MIN, INT32_MAX) + const microseconds = requireInt64(interval.microseconds, "interval microseconds") + return { months, days, microseconds } +} + +const intervalCodec: UnsafeCodec = codecOf( + (bytes, offset, size) => { + requireSize(size, 16, "interval") + stage8(bytes, offset) + return { + months: readInt32(bytes, offset + 12), + days: readInt32(bytes, offset + 8), + microseconds: scratchView8.getBigInt64(0) + } + }, + (value) => { + const interval = intervalValue(value) + const bytes = new Uint8Array(16) + scratchView8.setBigInt64(0, interval.microseconds) + bytes.set(scratchBytes8) + writeInt32(bytes, 8, interval.days) + writeInt32(bytes, 12, interval.months) + return bytes + }, + (sink, value) => { + const interval = intervalValue(value) + sink.bigInt64(interval.microseconds) + sink.int32(interval.days) + sink.int32(interval.months) + } +) + const builtinScalars = new Map>([ [ OID.bool, @@ -1255,10 +1319,10 @@ const builtinScalars = new Map>([ return scratchView8.getBigInt64(0) }, (value) => { - scratchView8.setBigInt64(0, int8Value(value)) + scratchView8.setBigInt64(0, requireInt64(value, "int8")) return takeScratch8() }, - (sink, value) => sink.bigInt64(int8Value(value)) + (sink, value) => sink.bigInt64(requireInt64(value, "int8")) ) ], [ @@ -1319,7 +1383,8 @@ const builtinScalars = new Map>([ [OID.date, dateCodec], [OID.timetz, timetzCodec], [OID.timestamp, timestampCodec], - [OID.timestamptz, timestampCodec] + [OID.timestamptz, timestampCodec], + [OID.interval, intervalCodec] ]) const makeArrayCodec = (elementOid: number, lookup: Lookup): UnsafeCodec> => @@ -1385,6 +1450,7 @@ const registerInState = ( /** * Creates a client-specific registry containing the built-in codecs. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1419,6 +1485,7 @@ const lookupFor = (registry: Registry | undefined): Lookup => * reading rows. For arrays, use `makeRegistry().register` with * `RegisterOptions.arrayOid` and pass the registry as the client's `types` option. * + * @stability unstable * @category registry * @since 4.0.0 */ @@ -1431,6 +1498,7 @@ export const register = (oid: number, codec: Codec): void => { /** * Removes a previously registered codec. * + * @stability unstable * @category registry * @since 4.0.0 */ @@ -1598,6 +1666,7 @@ const decodeArray = ( /** * A result column, as `RowDescription` describes one. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1626,6 +1695,7 @@ export interface Column { * parser.readField = Result.getOrThrow(PgTypes.makeFieldReader(description.fields)) * ``` * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -1658,6 +1728,7 @@ export const makeFieldReader = ( * Returns a `CodecError` failure when the value has the wrong JavaScript type, * or when the OID is neither built in nor registered. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1677,6 +1748,7 @@ export const encode = (value: unknown, oid: number, registry?: Registry): Result * text; invalid UTF-8 fails with `CodecError`. See `register` for binary * user-defined types, including arrays. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -1701,6 +1773,7 @@ export const decode = ( /** * The runtime type identifier for PostgreSQL parameters. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -1709,6 +1782,7 @@ export const ParameterTypeId: ParameterTypeId = "~@effect/sql-pg/PgTypes/Paramet /** * The type-level identifier for PostgreSQL parameters. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -1717,6 +1791,7 @@ export type ParameterTypeId = "~@effect/sql-pg/PgTypes/Parameter" /** * A value paired with the OID it should be encoded as. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1729,6 +1804,7 @@ export interface Parameter { /** * Returns whether a value is a parameter created by this module. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -1738,6 +1814,7 @@ export const isParameter = (value: unknown): value is Parameter => /** * Encodes a parameter for a `Bind` message. SQL NULL stays `null`. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1759,6 +1836,7 @@ const writeValue = (sink: ValueSink, value: unknown, oid: number, lookup: Lookup * Untyped parameters (OID `0`) use text so PostgreSQL can infer their type; * typed parameters use the binary format. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1784,6 +1862,7 @@ const writeParameterUnsafe = (sink: ValueSink, parameter: Parameter, lookup: Loo /** * Writes a parameter into a `Bind` frame. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -1817,6 +1896,7 @@ const parameter = (oid: number) => (value: unknown): Parameter => makeParameter( /** * A `bool` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1825,6 +1905,7 @@ export const bool: (value: boolean | null) => Parameter = parameter(OID.bool) /** * An `int2` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1833,6 +1914,7 @@ export const int2: (value: number | null) => Parameter = parameter(OID.int2) /** * An `int4` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1841,6 +1923,7 @@ export const int4: (value: number | null) => Parameter = parameter(OID.int4) /** * An `int8` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1849,6 +1932,7 @@ export const int8: (value: bigint | null) => Parameter = parameter(OID.int8) /** * An `oid` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1857,6 +1941,7 @@ export const oid: (value: number | null) => Parameter = parameter(OID.oid) /** * A `float4` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1865,6 +1950,7 @@ export const float4: (value: number | null) => Parameter = parameter(OID.float4) /** * A `float8` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1873,6 +1959,7 @@ export const float8: (value: number | null) => Parameter = parameter(OID.float8) /** * A `numeric` parameter, given as a decimal string or `"NaN"`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1881,6 +1968,7 @@ export const numeric: (value: string | null) => Parameter = parameter(OID.numeri /** * A `text` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1889,6 +1977,7 @@ export const text: (value: string | null) => Parameter = parameter(OID.text) /** * A `varchar` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1897,6 +1986,7 @@ export const varchar: (value: string | null) => Parameter = parameter(OID.varcha /** * A `bpchar` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1905,6 +1995,7 @@ export const bpchar: (value: string | null) => Parameter = parameter(OID.bpchar) /** * A `name` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1913,6 +2004,7 @@ export const name: (value: string | null) => Parameter = parameter(OID.name) /** * A `bytea` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1921,6 +2013,7 @@ export const bytea: (value: Uint8Array | null) => Parameter = parameter(OID.byte /** * A `json` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1929,6 +2022,7 @@ export const json: (value: unknown) => Parameter = parameter(OID.json) /** * A `jsonb` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1937,6 +2031,7 @@ export const jsonb: (value: unknown) => Parameter = parameter(OID.jsonb) /** * A `uuid` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1945,6 +2040,7 @@ export const uuid: (value: string | null) => Parameter = parameter(OID.uuid) /** * An `inet` parameter, such as `"10.0.0.1"` or `"10.0.0.0/8"`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1953,6 +2049,7 @@ export const inet: (value: string | null) => Parameter = parameter(OID.inet) /** * A `cidr` parameter. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1961,6 +2058,7 @@ export const cidr: (value: string | null) => Parameter = parameter(OID.cidr) /** * A `date` parameter, given as `YYYY-MM-DD`, `"infinity"`, or `"-infinity"`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1969,6 +2067,7 @@ export const date: (value: string | null) => Parameter = parameter(OID.date) /** * A `time` parameter, given as microseconds since midnight. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1977,6 +2076,7 @@ export const time: (value: bigint | null) => Parameter = parameter(OID.time) /** * A `timetz` parameter, such as `"12:34:56+02:00"`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1986,6 +2086,7 @@ export const timetz: (value: string | null) => Parameter = parameter(OID.timetz) * A `timestamp` parameter from a `Date` or epoch milliseconds. UTC fields * become the stored wall-clock fields, regardless of session `TimeZone`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1994,14 +2095,25 @@ export const timestamp: (value: Date | number | null) => Parameter = parameter(O /** * A `timestamptz` parameter, given as a `Date` or Unix epoch milliseconds. * + * @stability unstable * @category constructors * @since 4.0.0 */ export const timestamptz: (value: Date | number | null) => Parameter = parameter(OID.timestamptz) +/** + * An `interval` parameter with independent months, days, and microseconds. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const interval: (value: Interval | null) => Parameter = parameter(OID.interval) + /** * A one-dimensional array parameter whose elements have the given OID. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pg/src/index.ts b/repos/effect/packages/sql/pg/src/index.ts index ce2e8317c..99a4c3321 100644 --- a/repos/effect/packages/sql/pg/src/index.ts +++ b/repos/effect/packages/sql/pg/src/index.ts @@ -1,40 +1,48 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as PgAuth from "./PgAuth.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgClient from "./PgClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgConnection from "./PgConnection.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgMigrator from "./PgMigrator.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgPool from "./PgPool.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgProtocol from "./PgProtocol.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgTypes from "./PgTypes.ts" diff --git a/repos/effect/packages/sql/pg/test/PgConnection.integration.test.ts b/repos/effect/packages/sql/pg/test/PgConnection.integration.test.ts index 366596662..4c4413340 100644 --- a/repos/effect/packages/sql/pg/test/PgConnection.integration.test.ts +++ b/repos/effect/packages/sql/pg/test/PgConnection.integration.test.ts @@ -245,6 +245,35 @@ it.layer(PgContainer.layer, { timeout: "30 seconds" })("PgConnection", (it) => { assert.deepStrictEqual((yield* connection.query("SELECT $1::int4 AS n", [8])).rows, [{ n: 8 }]) })) + it.effect("preserves a stale-plan error in a transaction and reparses after rollback", () => + Effect.gen(function*() { + const connection = yield* makeConnection() + yield* connection.query("CREATE TEMP TABLE stale_in_transaction (a int4)") + yield* connection.query("INSERT INTO stale_in_transaction VALUES (1)") + yield* connection.query("SELECT * FROM stale_in_transaction") + yield* connection.query("ALTER TABLE stale_in_transaction ADD COLUMN b text DEFAULT 'added'") + yield* connection.query("BEGIN") + const error = yield* Effect.flip(connection.query("SELECT * FROM stale_in_transaction")) + assert.propertyVal(error.reason.cause, "code", "0A000") + yield* connection.query("ROLLBACK") + assert.deepStrictEqual((yield* connection.query("SELECT * FROM stale_in_transaction")).rows, [{ + a: 1, + b: "added" + }]) + })) + + it.effect("preserves a missing-statement error in a transaction and reparses after rollback", () => + Effect.gen(function*() { + const connection = yield* makeConnection() + yield* connection.query("SELECT $1::int4 AS n", [7]) + yield* connection.query("DEALLOCATE ALL") + yield* connection.query("BEGIN") + const error = yield* Effect.flip(connection.query("SELECT $1::int4 AS n", [8])) + assert.propertyVal(error.reason.cause, "code", "26000") + yield* connection.query("ROLLBACK") + assert.deepStrictEqual((yield* connection.query("SELECT $1::int4 AS n", [9])).rows, [{ n: 9 }]) + })) + it.effect("closes statements it evicts from a full cache", () => Effect.gen(function*() { const connection = yield* makeConnection({ preparedStatementCacheSize: 3 }) diff --git a/repos/effect/packages/sql/pg/test/PgPool.integration.test.ts b/repos/effect/packages/sql/pg/test/PgPool.integration.test.ts index 05e5f51cf..7207ccd6b 100644 --- a/repos/effect/packages/sql/pg/test/PgPool.integration.test.ts +++ b/repos/effect/packages/sql/pg/test/PgPool.integration.test.ts @@ -1,6 +1,6 @@ -import { PgConnection, PgPool } from "@effect/sql-pg" +import { PgConnection, PgPool, PgTypes } from "@effect/sql-pg" import { assert, it } from "@effect/vitest" -import { Effect, Fiber, Queue, Redacted, Stream } from "effect" +import { Effect, Fiber, Queue, Redacted, Result, Stream } from "effect" import * as Net from "node:net" import { Duplex } from "node:stream" import { PgContainer } from "./utils.ts" @@ -15,20 +15,31 @@ const poolConfig = Effect.gen(function*() { return { url: Redacted.make(container.getConnectionUri()) } }) -const waitUntilActive = (observer: PgConnection.PgConnection, pid: number) => +const waitUntilActive = (observer: PgConnection.PgConnection, pid: number, advisoryLock = false) => Effect.gen(function*() { const startedAt = Date.now() while (Date.now() - startedAt < activeTimeoutMs) { const active = yield* observer.query( - "SELECT count(*)::int4 AS active FROM pg_stat_activity WHERE pid = $1 AND state = 'active'", - [pid] + `SELECT count(*)::int4 AS active FROM pg_stat_activity + WHERE pid = $1 AND state = 'active' + AND (NOT $2::boolean OR (wait_event_type = 'Lock' AND wait_event = 'advisory'))`, + [pid, advisoryLock] ) if (active.rows[0].active === 1) return yield* realSleep } - return yield* Effect.fail(new Error(`PostgreSQL backend ${pid} did not become active within 10 seconds`)) + return yield* Effect.fail( + new Error( + `PostgreSQL backend ${pid} did not become ${ + advisoryLock ? "blocked on an advisory lock" : "active" + } within 10 seconds` + ) + ) }) +// Parse can report active before Execute starts. Cancellation needs the query to be waiting on the lock. +const waitUntilAdvisoryLock = (observer: PgConnection.PgConnection, pid: number) => waitUntilActive(observer, pid, true) + const cancelRequestCode = 80877102 /** Holds `CancelRequest` messages until the test releases them. */ @@ -248,21 +259,23 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const pool = yield* PgPool.make({ ...config, maxConnections: 1 }) const blocker = yield* PgConnection.make(config) const observer = yield* PgConnection.make(config) - const first = yield* Effect.scoped(Effect.gen(function*() { - const connection = yield* pool.get - yield* blocker.query("BEGIN") - yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - const fiber = yield* Effect.forkScoped( - connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - ) - yield* waitUntilActive(observer, connection.processId) - yield* connection.interrupt - const error = yield* Effect.flip(Fiber.join(fiber)) - assert.strictEqual(error._tag, "SqlError") - assert.strictEqual(error.reason._tag, "StatementTimeoutError") - yield* blocker.query("ROLLBACK") - return connection.processId - })) + // Roll back inside the scope, before it interrupts/joins the query fiber on failure. + const first = yield* Effect.scoped( + Effect.gen(function*() { + const connection = yield* pool.get + yield* blocker.query("BEGIN") + yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + const fiber = yield* Effect.forkScoped( + connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + ) + yield* waitUntilAdvisoryLock(observer, connection.processId) + yield* connection.interrupt + const error = yield* Effect.flip(Fiber.join(fiber)) + assert.strictEqual(error._tag, "SqlError") + assert.strictEqual(error.reason._tag, "StatementTimeoutError") + return connection.processId + }).pipe(Effect.ensuring(Effect.ignore(blocker.query("ROLLBACK")))) + ) const second = yield* Effect.scoped(Effect.map(pool.get, (connection) => connection.processId)) assert.strictEqual(second, first) @@ -277,20 +290,22 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const observer = yield* PgConnection.make(config) yield* Effect.gen(function*() { - const first = yield* Effect.scoped(Effect.gen(function*() { - const connection = yield* pool.get - yield* blocker.query("BEGIN") - yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - const query = yield* Effect.forkScoped( - connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - ) - yield* waitUntilActive(observer, connection.processId) - const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) - yield* gate.intercepted - yield* blocker.query("COMMIT") - yield* Fiber.join(interruption) - return connection.processId - })) + const first = yield* Effect.scoped( + Effect.gen(function*() { + const connection = yield* pool.get + yield* blocker.query("BEGIN") + yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + const query = yield* Effect.forkScoped( + connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + ) + yield* waitUntilAdvisoryLock(observer, connection.processId) + const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) + yield* gate.intercepted + yield* blocker.query("COMMIT") + yield* Fiber.join(interruption) + return connection.processId + }).pipe(Effect.ensuring(Effect.ignore(blocker.query("ROLLBACK")))) + ) const second = yield* Effect.scoped(Effect.map(pool.get, (connection) => connection.processId)) assert.notStrictEqual(second, first) @@ -308,20 +323,22 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const observer = yield* PgConnection.make(config) yield* Effect.gen(function*() { - const first = yield* Effect.scoped(Effect.gen(function*() { - const connection = yield* pool.get - yield* blocker.query("BEGIN") - yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - const query = yield* Effect.forkScoped( - connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - ) - yield* waitUntilActive(observer, connection.processId) - const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) - yield* gate.intercepted - yield* blocker.query("COMMIT") - yield* Fiber.join(interruption) - return connection.processId - })) + const first = yield* Effect.scoped( + Effect.gen(function*() { + const connection = yield* pool.get + yield* blocker.query("BEGIN") + yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + const query = yield* Effect.forkScoped( + connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + ) + yield* waitUntilAdvisoryLock(observer, connection.processId) + const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) + yield* gate.intercepted + yield* blocker.query("COMMIT") + yield* Fiber.join(interruption) + return connection.processId + }).pipe(Effect.ensuring(Effect.ignore(blocker.query("ROLLBACK")))) + ) const second = yield* pool.use((connection) => Effect.succeed(connection.processId)) assert.notStrictEqual(second, first) @@ -341,19 +358,21 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const lockAcquired = yield* Queue.unbounded() yield* Effect.gen(function*() { - yield* Effect.scoped(Effect.gen(function*() { - const connection = yield* pool.get - yield* blocker.query("BEGIN") - yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - const query = yield* Effect.forkScoped( - connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - ) - yield* waitUntilActive(observer, connection.processId) - const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) - yield* gate.intercepted - yield* blocker.query("COMMIT") - yield* Fiber.join(interruption) - })) + yield* Effect.scoped( + Effect.gen(function*() { + const connection = yield* pool.get + yield* blocker.query("BEGIN") + yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + const query = yield* Effect.forkScoped( + connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + ) + yield* waitUntilAdvisoryLock(observer, connection.processId) + const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) + yield* gate.intercepted + yield* blocker.query("COMMIT") + yield* Fiber.join(interruption) + }).pipe(Effect.ensuring(Effect.ignore(blocker.query("ROLLBACK")))) + ) const followUp = yield* Effect.forkScoped( pool.use((connection) => @@ -371,7 +390,7 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo yield* blocker.query("BEGIN") yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [followUpPid]) yield* Queue.offer(lockAcquired, undefined) - yield* waitUntilActive(observer, followUpPid) + yield* waitUntilAdvisoryLock(observer, followUpPid) gate.release() yield* gate.delivered yield* blocker.query("COMMIT") @@ -429,19 +448,21 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const blocker = yield* PgConnection.make(config) yield* Effect.gen(function*() { - const first = yield* Effect.scoped(Effect.gen(function*() { - const connection = yield* pool.get - yield* blocker.query("BEGIN") - yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) - const stream = yield* Effect.forkScoped( - connection.stream(blockedStream, [connection.processId]).pipe(Stream.take(1), Stream.runCollect) - ) - yield* gate.intercepted - yield* blocker.query("COMMIT") - const rows = yield* Fiber.join(stream) - assert.deepStrictEqual(rows, [{ n: 1 }]) - return connection.processId - })) + const first = yield* Effect.scoped( + Effect.gen(function*() { + const connection = yield* pool.get + yield* blocker.query("BEGIN") + yield* blocker.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) + const stream = yield* Effect.forkScoped( + connection.stream(blockedStream, [connection.processId]).pipe(Stream.take(1), Stream.runCollect) + ) + yield* gate.intercepted + yield* blocker.query("COMMIT") + const rows = yield* Fiber.join(stream) + assert.deepStrictEqual(rows, [{ n: 1 }]) + return connection.processId + }).pipe(Effect.ensuring(Effect.ignore(blocker.query("ROLLBACK")))) + ) yield* Effect.scoped(Effect.gen(function*() { const replacement = yield* pool.get @@ -490,7 +511,7 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo const query = yield* Effect.forkScoped( connection.query("SELECT pg_advisory_xact_lock($1::int4)", [connection.processId]) ) - yield* waitUntilActive(observer, connection.processId) + yield* waitUntilAdvisoryLock(observer, connection.processId) const interruption = yield* Effect.forkScoped(Fiber.interrupt(query)) yield* gate.intercepted yield* blocker.query("COMMIT") @@ -502,7 +523,14 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo yield* gate.delivered const afterDelivery = yield* connection.query("SELECT pg_backend_pid()::int4 AS pid") assert.deepStrictEqual(afterDelivery.rows, [{ pid: connection.processId }]) - }).pipe(Effect.ensuring(Effect.sync(() => gate.release()))) + }).pipe( + Effect.ensuring( + Effect.sync(() => gate.release()).pipe( + Effect.andThen(blocker.query("ROLLBACK")), + Effect.ignore + ) + ) + ) }), cancellationTestTimeout) it.effect("replaces connections that die", () => @@ -608,10 +636,13 @@ it.layer(PgContainer.layer, { timeout: "30 seconds", concurrent: false })("PgPoo it.effect("replaces a fatal session before the next pool.use query", () => Effect.gen(function*() { - const pool = yield* PgPool.make({ ...(yield* poolConfig), maxConnections: 1 }) + const failure = Result.fail(new PgTypes.CodecError({ message: "Intentional decode failure" })) + const types = PgTypes.makeRegistry() + types.register(PgTypes.OID.interval, { encode: () => failure, decode: () => failure }) + const pool = yield* PgPool.make({ ...(yield* poolConfig), maxConnections: 1, types }) const first = yield* pool.use((connection) => Effect.succeed(connection.processId)) - // Decoding this interval fails fatally in the connection's data handler. + // The failing codec errors fatally in the connection's data handler. // The next borrow must not see the dead session, even in the same fiber. const error = yield* Effect.flip(pool.use((connection) => connection.query("SELECT interval '-1 day' AS v"))) assert.strictEqual(error._tag, "SqlError") diff --git a/repos/effect/packages/sql/pg/test/PgTypes.test.ts b/repos/effect/packages/sql/pg/test/PgTypes.test.ts index fbd5feb83..f9d9652db 100644 --- a/repos/effect/packages/sql/pg/test/PgTypes.test.ts +++ b/repos/effect/packages/sql/pg/test/PgTypes.test.ts @@ -167,6 +167,83 @@ const binary = (oids: ReadonlyArray): Array => oids.map((dataTypeOid) => ({ dataTypeOid, format: 1 })) describe("PgTypes", () => { + describe("intervals", () => { + // PostgreSQL interval_send: int64 microseconds, int32 days, int32 months. + // Independent fixtures for the four values reported in #8743. + const wires = [ + "000000036c8bc0800000000300000000", + "0000000000000000000000000000000e", + "0000000000000000ffffffff00000000", + "000000000016e3600000000000000000" + ] + const values = [ + { months: 0, days: 3, microseconds: 14706000000n }, + { months: 14, days: 0, microseconds: 0n }, + { months: 0, days: -1, microseconds: 0n }, + { months: 0, days: 0, microseconds: 1500000n } + ] + + it("reads the reported intervals alongside text and SQL NULL", () => { + const { interval, text } = PgTypes.OID + const parser = PgProtocol.makeParser({ + readField: PgTypes.makeFieldReader(binary([interval, interval, interval, interval, text, interval])) + }) + const row = parser.push(dataRow([...wires.map(bytes), bytes("6f6b"), null]))[0] as PgProtocol.DataRow + assert.deepStrictEqual(row.values, [...values, "ok", null]) + }) + + it("encodes and decodes an interval array with a NULL element", () => { + const value = [values[0], null, values[2]] + const wire = bytes( + "0000000100000001000004a20000000300000001" + + "00000010000000036c8bc0800000000300000000" + + "ffffffff" + + "000000100000000000000000ffffffff00000000" + ) + assert.deepStrictEqual(PgTypes.encodeParameter(PgTypes.array(value, PgTypes.OID.interval)), wire) + assert.deepStrictEqual(PgTypes.decode(wire, PgTypes.OID.intervalArray, 1), value) + }) + + it("preserves signed int32 and int64 boundaries without flattening calendar units", () => { + for ( + const { value, wire } of [ + { + wire: "80000000000000007fffffff80000000", + value: { months: -2147483648, days: 2147483647, microseconds: -9223372036854775808n } + }, + { + wire: "7fffffffffffffff800000007fffffff", + value: { months: 2147483647, days: -2147483648, microseconds: 9223372036854775807n } + } + ] + ) { + assert.deepStrictEqual(PgTypes.decode(bytes(wire), PgTypes.OID.interval, 1), value) + assert.deepStrictEqual(PgTypes.encodeParameter(PgTypes.interval(value)), bytes(wire)) + } + }) + + it("requires exactly sixteen payload bytes", () => { + for (const size of [15, 17]) { + assertThrowsTagged( + "PgTypesCodecError", + () => PgTypesResult.decode(new Uint8Array(size), PgTypes.OID.interval, 1) + ) + } + }) + + it("rejects out-of-range calendar units and invalid microseconds", () => { + for ( + const value of [ + { months: -2147483649, days: 0, microseconds: 0n }, + { months: 0, days: 2147483648, microseconds: 0n }, + { months: 0, days: 0, microseconds: 9223372036854775808n }, + { months: 0, days: 0, microseconds: 1 } + ] + ) { + assertThrowsTagged("PgTypesCodecError", () => PgTypesResult.encode(value, PgTypes.OID.interval)) + } + }) + }) it("returns codec failures as Result values", () => { for ( const result of [ diff --git a/repos/effect/packages/sql/pg/test/SqlEventJournal.integration.test.ts b/repos/effect/packages/sql/pg/test/SqlEventJournal.integration.test.ts new file mode 100644 index 000000000..b78f82fe2 --- /dev/null +++ b/repos/effect/packages/sql/pg/test/SqlEventJournal.integration.test.ts @@ -0,0 +1,58 @@ +import { assert, it } from "@effect/vitest" +import { Effect } from "effect" +import * as EventJournal from "effect/eventlog/EventJournal" +import * as SqlEventJournal from "effect/eventlog/SqlEventJournal" +import { PgContainer } from "./utils.ts" + +it.layer(PgContainer.layerClient, { timeout: "30 seconds" })("SqlEventJournal", (it) => { + it.effect("round-trips a local entry", () => + Effect.gen(function*() { + const journal = yield* SqlEventJournal.make({ + entryTable: "local_entries", + remotesTable: "local_remotes" + }) + const written = yield* journal.write({ + event: "UserCreated", + primaryKey: "user-1", + payload: new Uint8Array([1]), + effect: (entry) => Effect.succeed(entry) + }) + const entries = yield* journal.entries + assert.strictEqual(entries.length, 1) + assert.strictEqual(entries[0].idString, written.idString) + assert.strictEqual(entries[0].event, "UserCreated") + assert.strictEqual(entries[0].primaryKey, "user-1") + assert.deepStrictEqual(entries[0].payload, new Uint8Array([1])) + assert.strictEqual(entries[0].createdAtMillis, written.createdAtMillis) + })) + + it.effect("round-trips a remote entry and advances its sequence", () => + Effect.gen(function*() { + const journal = yield* SqlEventJournal.make({ + entryTable: "remote_entries", + remotesTable: "remote_remotes" + }) + const remoteId = EventJournal.makeRemoteIdUnsafe() + const remoteEntry = new EventJournal.Entry({ + id: EventJournal.makeEntryIdUnsafe({ msecs: 1_700_000_000_000 }), + event: "UserCreated", + primaryKey: "user-2", + payload: new Uint8Array([2]) + }, { disableChecks: true }) + assert.strictEqual(yield* journal.nextRemoteSequence(remoteId), 0) + yield* journal.writeFromRemote({ + remoteId, + entries: [new EventJournal.RemoteEntry({ remoteSequence: 0, entry: remoteEntry })], + effect: () => Effect.void + }) + + const entries = yield* journal.entries + assert.strictEqual(entries.length, 1) + assert.strictEqual(entries[0].idString, remoteEntry.idString) + assert.strictEqual(entries[0].event, "UserCreated") + assert.strictEqual(entries[0].primaryKey, "user-2") + assert.deepStrictEqual(entries[0].payload, new Uint8Array([2])) + assert.strictEqual(entries[0].createdAtMillis, 1_700_000_000_000) + assert.strictEqual(yield* journal.nextRemoteSequence(remoteId), 1) + })) +}) diff --git a/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts b/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts index ddfc59618..4f18896a7 100644 --- a/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts +++ b/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts @@ -1,7 +1,132 @@ -import * as SqlEventLogServerUnencryptedStorageTest from "effect-test/eventlog/SqlEventLogServerUnencryptedStorageTest" +import { assert, it } from "@effect/vitest" +import { Deferred, Effect, Fiber, Layer, Option, Queue } from "effect" +import { + makeEntry, + makeOptions, + makeStorage, + makeStoreId, + openChanges, + suite +} from "effect-test/eventlog/SqlEventLogServerUnencryptedStorageTest" +import { Reactivity } from "effect/reactivity" +import { PreventSchedulerYield } from "effect/Scheduler" +import * as SqlClient from "effect/sql/SqlClient" import { PgContainer } from "./utils.ts" -SqlEventLogServerUnencryptedStorageTest.suite( - "sql-pg", - PgContainer.layerClient +suite("sql-pg", PgContainer.layerClient) + +it.layer(Layer.merge(Reactivity.layer, PgContainer.layerClient), { timeout: "30 seconds" })( + "SqlEventLogServerUnencrypted commit visibility", + (it) => { + it.effect("streams concurrent commits in sequence order even when the first SQL return is delayed", () => + Effect.gen(function*() { + const sql = yield* SqlClient.SqlClient + const committed = yield* Deferred.make() + const publish = yield* Deferred.make() + // Pause after SQL commits and releases its connection, before storage publishes. + let pauseNext = false + let observeSecond = false + let secondEnteredSql = false + const delayedSql: SqlClient.SqlClient = Object.assign( + (...args: Parameters) => sql(...args), + sql, + { + withTransaction: (effect: Effect.Effect) => + Effect.suspend(() => { + if (observeSecond) secondEnteredSql = true + const pause = pauseNext + pauseNext = false + return sql.withTransaction(effect).pipe( + Effect.tap(() => + pause + ? Deferred.succeed(committed, undefined).pipe(Effect.andThen(Deferred.await(publish))) + : Effect.void + ) + ) + }) + } + ) + const storage = yield* makeStorage(makeOptions("concurrent_publication")).pipe( + Effect.provideService(SqlClient.SqlClient, delayedSql) + ) + const storeId = makeStoreId("concurrent_publication") + const backlog = makeEntry("Ada") + const firstEntry = makeEntry("Grace") + const secondEntry = makeEntry("Margaret") + yield* storage.write(storeId, [backlog]) + const changes = yield* openChanges(storage, storeId) + assert.strictEqual((yield* Queue.take(changes)).remoteSequence, 1) + + pauseNext = true + const firstWriter = yield* storage.write(storeId, [firstEntry]).pipe(Effect.forkChild) + yield* Deferred.await(committed) + // Run writer two until it enters SQL or waits for the storage permit. + observeSecond = true + const secondWriter = yield* storage.write(storeId, [secondEntry]).pipe( + Effect.provideService(PreventSchedulerYield, true), + Effect.forkChild({ startImmediately: true }) + ) + observeSecond = false + if (secondEnteredSql) { + // Without serialization, force writer two to publish first. + yield* Fiber.join(secondWriter).pipe( + Effect.ensuring(Deferred.succeed(publish, undefined)) + ) + } else { + // Release writer one so writer two can acquire the permit. + yield* Deferred.succeed(publish, undefined) + yield* Fiber.join(secondWriter) + } + yield* Fiber.join(firstWriter) + + const rows = yield* Queue.takeN(changes, 2) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + const persisted = yield* Queue.takeAll(yield* openChanges(storage, storeId)) + assert.deepStrictEqual(persisted.map((row) => row.remoteSequence), [1, 2, 3]) + assert.deepStrictEqual( + persisted.map((row) => row.entry.idString), + [backlog.idString, firstEntry.idString, secondEntry.idString] + ) + assert.deepStrictEqual(rows.map((row) => row.remoteSequence), [2, 3]) + assert.deepStrictEqual(rows.map((row) => row.entry.idString), [firstEntry.idString, secondEntry.idString]) + })) + + it.effect("streams a write committed after the startup backlog read exactly once", () => + Effect.gen(function*() { + const storage = yield* makeStorage(makeOptions("commit_visibility")) + const storeId = makeStoreId("commit_visibility") + const backlogEntry = makeEntry("Ada") + const racedEntry = makeEntry("Grace") + const liveEntry = makeEntry("Margaret") + yield* storage.write(storeId, [backlogEntry]) + + const written = yield* Deferred.make() + const commit = yield* Deferred.make() + const writer = yield* storage.withTransaction(Effect.gen(function*() { + yield* storage.write(storeId, [racedEntry]) + yield* Deferred.succeed(written, undefined) + yield* Deferred.await(commit) + })).pipe(Effect.forkChild) + yield* Deferred.await(written) + + // Read the backlog before commit to establish the subscription and snapshot. + const changes = yield* openChanges(storage, storeId) + const first = yield* Queue.take(changes) + assert.strictEqual(first.entry.idString, backlogEntry.idString) + + yield* Deferred.succeed(commit, undefined) + yield* Fiber.join(writer) + // A later write exposes a missed row as [1, 3] instead of a timeout. + yield* storage.write(storeId, [liveEntry]) + const second = yield* Queue.take(changes) + assert.deepStrictEqual([first.remoteSequence, second.remoteSequence], [1, 2]) + assert.strictEqual(second.entry.idString, racedEntry.idString) + const third = yield* Queue.take(changes) + assert.strictEqual(third.remoteSequence, 3) + assert.strictEqual(third.entry.idString, liveEntry.idString) + yield* Effect.yieldNow + assert.strictEqual(Option.isNone(yield* Queue.poll(changes)), true) + })) + } ) diff --git a/repos/effect/packages/sql/pglite/CHANGELOG.md b/repos/effect/packages/sql/pglite/CHANGELOG.md index c9af5e1ec..2017f7c0e 100644 --- a/repos/effect/packages/sql/pglite/CHANGELOG.md +++ b/repos/effect/packages/sql/pglite/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-pglite +## 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/sql/pglite/package.json b/repos/effect/packages/sql/pglite/package.json index e2c851b31..c1341bbcb 100644 --- a/repos/effect/packages/sql/pglite/package.json +++ b/repos/effect/packages/sql/pglite/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-pglite", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A PGlite toolkit for Effect", diff --git a/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts b/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts index 555108e9b..3c2973c0c 100644 --- a/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts +++ b/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts @@ -6,6 +6,7 @@ * `SqlClient`. It does not require a separate PGlite service; the active SQL * client supplies the database connection. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -22,6 +23,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -36,6 +38,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/pglite/src/index.ts b/repos/effect/packages/sql/pglite/src/index.ts index c89ffb5cc..e38209582 100644 --- a/repos/effect/packages/sql/pglite/src/index.ts +++ b/repos/effect/packages/sql/pglite/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,6 +12,7 @@ export * as PgliteClient from "./PgliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as PgliteMigrator from "./PgliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md b/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md index 4cf5018ab..8e74d7252 100644 --- a/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md +++ b/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-sqlite-bun +## 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/sql/sqlite-bun/package.json b/repos/effect/packages/sql/sqlite-bun/package.json index bab05044a..844d6ba7c 100644 --- a/repos/effect/packages/sql/sqlite-bun/package.json +++ b/repos/effect/packages/sql/sqlite-bun/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-sqlite-bun", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A SQLite toolkit for Effect", diff --git a/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts b/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts index e243dc3f6..2f813742d 100644 --- a/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts +++ b/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts @@ -11,6 +11,7 @@ * synchronous. Database export and extension loading are supported; streaming * queries and `updateValues` are not. * + * @stability unstable * @since 4.0.0 */ import { constants, Database } from "bun:sqlite" @@ -41,6 +42,7 @@ const classifyError = (cause: unknown, message: string, operation: string) => /** * Runtime type identifier used to mark Bun `SqliteClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -49,6 +51,7 @@ export const TypeId: TypeId = "~@effect/sql-sqlite-bun/SqliteClient" /** * Type-level identifier used to mark Bun `SqliteClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -57,6 +60,7 @@ export type TypeId = "~@effect/sql-sqlite-bun/SqliteClient" /** * Bun SQLite client service, extending `SqlClient` with database export and extension loading helpers. `updateValues` is not supported. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -77,6 +81,7 @@ export interface SqliteClient extends Client.SqlClient { * * Use to access or provide a Bun SQLite client through the Effect context. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -85,6 +90,7 @@ export const SqliteClient = Context.Service("@effect/sql-sqlite-bu /** * Configuration for a Bun SQLite client, including filename, open mode flags, WAL and busy timeout behavior, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -116,6 +122,7 @@ interface SqliteConnection extends Connection { /** * Creates a scoped Bun SQLite client for a database file, enabling WAL and a 5-second busy timeout by default. Explicit transactions on writable connections take the write lock for their duration, even when they only read; clients opened with `readonly: true` are unaffected. Streaming queries are not implemented. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -293,6 +300,7 @@ export const make = ( /** * Creates a layer from a `Config`-wrapped Bun SQLite client configuration, providing both `SqliteClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -313,6 +321,7 @@ export const layerConfig = ( /** * Creates a layer from a concrete Bun SQLite client configuration, providing both `SqliteClient` and `SqlClient`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts b/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts index c4139b7f7..e3fa4d362 100644 --- a/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts +++ b/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts @@ -6,6 +6,7 @@ * `SqlClient`. It does not add Bun-specific schema dump support; migration * execution is handled by the shared SQL migrator. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -22,6 +23,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -78,6 +80,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-bun/src/index.ts b/repos/effect/packages/sql/sqlite-bun/src/index.ts index 986ce8399..210d7ee08 100644 --- a/repos/effect/packages/sql/sqlite-bun/src/index.ts +++ b/repos/effect/packages/sql/sqlite-bun/src/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as SqliteClient from "./SqliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteMigrator from "./SqliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-do/CHANGELOG.md b/repos/effect/packages/sql/sqlite-do/CHANGELOG.md index 73f09f536..ffd0f9c43 100644 --- a/repos/effect/packages/sql/sqlite-do/CHANGELOG.md +++ b/repos/effect/packages/sql/sqlite-do/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-sqlite-do +## 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/sql/sqlite-do/package.json b/repos/effect/packages/sql/sqlite-do/package.json index 96a45f7c6..e894c05db 100644 --- a/repos/effect/packages/sql/sqlite-do/package.json +++ b/repos/effect/packages/sql/sqlite-do/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-sqlite-do", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A SQLite toolkit for Effect", @@ -61,7 +61,7 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@cloudflare/workers-types": "^5.20260926.1", + "@cloudflare/workers-types": "^5.20261003.1", "effect": "workspace:^", "esbuild": "^0.28.2", "miniflare": "5.20260925.0-alpha" diff --git a/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts b/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts index ebaec1c40..19bb4a9c2 100644 --- a/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts +++ b/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts @@ -22,6 +22,7 @@ * migration layer has finished. This adapter does not currently write SQLite * schema dumps for `schemaDirectory`. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -38,6 +39,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -52,6 +54,7 @@ export const run: ( /** * Creates a layer that runs the configured SQL migrations during layer construction. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-do/src/index.ts b/repos/effect/packages/sql/sqlite-do/src/index.ts index e281b0390..210d7ee08 100644 --- a/repos/effect/packages/sql/sqlite-do/src/index.ts +++ b/repos/effect/packages/sql/sqlite-do/src/index.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ @@ -11,6 +12,7 @@ export * as SqliteClient from "./SqliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteMigrator from "./SqliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-node/CHANGELOG.md b/repos/effect/packages/sql/sqlite-node/CHANGELOG.md index 1fb855a1b..897ec402d 100644 --- a/repos/effect/packages/sql/sqlite-node/CHANGELOG.md +++ b/repos/effect/packages/sql/sqlite-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-sqlite-node +## 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/sql/sqlite-node/package.json b/repos/effect/packages/sql/sqlite-node/package.json index 306125d4d..ddf918c3c 100644 --- a/repos/effect/packages/sql/sqlite-node/package.json +++ b/repos/effect/packages/sql/sqlite-node/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-sqlite-node", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A SQLite toolkit for Effect", @@ -62,7 +62,7 @@ }, "devDependencies": { "@effect/platform-node": "workspace:^", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "effect": "workspace:^" }, "peerDependencies": { diff --git a/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts b/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts index 5192b94f1..7682a4d48 100644 --- a/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts +++ b/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts @@ -12,6 +12,7 @@ * and extension loading are supported; streaming queries and `updateValues` * are not. * + * @stability unstable * @since 4.0.0 */ import * as Cache from "effect/Cache" @@ -42,6 +43,7 @@ const NANOS_PER_MILLI = BigInt(1_000_000) /** * Runtime type identifier used to mark Node `SqliteClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -50,6 +52,7 @@ export const TypeId: TypeId = "~@effect/sql-sqlite-node/SqliteClient" /** * Type-level identifier used to mark Node `SqliteClient` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -58,6 +61,7 @@ export type TypeId = "~@effect/sql-sqlite-node/SqliteClient" /** * Node SQLite client service, extending `SqlClient` with database export, backup, and extension loading helpers. `updateValues` is not supported. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -74,6 +78,7 @@ export interface SqliteClient extends Client.SqlClient { /** * Metadata returned from a Node SQLite backup operation, reporting total and remaining page counts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -85,6 +90,7 @@ export interface BackupMetadata { /** * Service tag for the node SQLite client implementation. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -93,6 +99,7 @@ export const SqliteClient = Context.Service("@effect/sql-sqlite-no /** * Configuration for a node SQLite client backed by `node:sqlite`, including the database filename, read-only mode, statement cache settings, WAL and busy timeout behavior, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -123,6 +130,7 @@ interface SqliteConnection extends Connection { /** * Creates a scoped node SQLite client from the supplied configuration, using a single serialized connection with WAL and a 5-second busy timeout enabled by default. Explicit transactions on writable connections take the write lock for their duration, even when they only read; clients opened with `readonly: true` are unaffected. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -358,6 +366,7 @@ export const make = ( /** * Builds a layer from an Effect `Config` value, providing both the node `SqliteClient` service and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -378,6 +387,7 @@ export const layerConfig = ( /** * Builds a layer from a node SQLite client configuration, providing both `SqliteClient` and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts b/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts index 3b3ebf25c..65a9cf00a 100644 --- a/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts +++ b/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts @@ -6,6 +6,7 @@ * `SqlClient`. It does not add Node-specific schema dump support; migration * execution is handled by the shared SQL migrator. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -22,6 +23,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations for a SQLite database using the shared `Migrator` implementation and the current `SqlClient`. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -78,6 +80,7 @@ export const run: ( /** * Creates a layer that runs the configured SQLite migrations during layer construction and provides no services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-node/src/index.ts b/repos/effect/packages/sql/sqlite-node/src/index.ts index 986ce8399..210d7ee08 100644 --- a/repos/effect/packages/sql/sqlite-node/src/index.ts +++ b/repos/effect/packages/sql/sqlite-node/src/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as SqliteClient from "./SqliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteMigrator from "./SqliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md b/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md index ebf09e852..d75188726 100644 --- a/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md +++ b/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-sqlite-react-native +## 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/sql/sqlite-react-native/package.json b/repos/effect/packages/sql/sqlite-react-native/package.json index 002db5b12..590b49589 100644 --- a/repos/effect/packages/sql/sqlite-react-native/package.json +++ b/repos/effect/packages/sql/sqlite-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-sqlite-react-native", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A SQLite toolkit for Effect", diff --git a/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts b/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts index 7943e423a..2280c732c 100644 --- a/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts +++ b/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts @@ -9,6 +9,7 @@ * to the driver's asynchronous query API. Streaming queries and `updateValues` * are not supported by this driver. * + * @stability unstable * @since 4.0.0 */ import * as Sqlite from "@op-engineering/op-sqlite" @@ -46,6 +47,7 @@ const classifyError = (cause: unknown, message: string, operation: string) => /** * Runtime identifier attached to SQLite React Native client values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -54,6 +56,7 @@ export const TypeId: TypeId = "~@effect/sql-sqlite-react-native/SqliteClient" /** * Type-level identifier for SQLite React Native client values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -62,6 +65,7 @@ export type TypeId = "~@effect/sql-sqlite-react-native/SqliteClient" /** * React Native SQLite client service interface, extending `SqlClient` with its configuration and marking `updateValues` as unsupported for SQLite. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -76,6 +80,7 @@ export interface SqliteClient extends Client.SqlClient { /** * Service tag for the React Native SQLite client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -84,6 +89,7 @@ export const SqliteClient = Context.Service("@effect/sql-sqlite-re /** * Configuration for a React Native SQLite client, including the database filename, optional location and encryption key, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -104,6 +110,7 @@ export interface SqliteClientConfig { * Use to switch React Native SQLite query execution to the asynchronous driver * API for a scoped effect. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -115,6 +122,7 @@ export const AsyncQuery = Context.Reference( /** * Runs an effect with `AsyncQuery` enabled, causing React Native SQLite queries in that effect to use the asynchronous driver API. * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -126,6 +134,7 @@ interface SqliteConnection extends Connection {} /** * Creates a scoped React Native SQLite client from the supplied configuration, using a single serialized connection and honoring `AsyncQuery` for query execution. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -246,6 +255,7 @@ export const make = ( /** * Builds a layer from an Effect `Config` value, providing both the React Native `SqliteClient` service and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -266,6 +276,7 @@ export const layerConfig = ( /** * Builds a layer from a React Native SQLite client configuration, providing both `SqliteClient` and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts b/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts index 93fe3d59a..d164fdd23 100644 --- a/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts +++ b/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts @@ -17,6 +17,7 @@ * suspension or process death, so keep migrations transaction-aware and avoid * assuming a fresh database on every launch. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -33,6 +34,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations for a React Native SQLite database using the shared `Migrator` implementation and the current `SqlClient`. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const run: ( /** * Creates a layer that runs the configured React Native SQLite migrations during layer construction and provides no services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-react-native/src/index.ts b/repos/effect/packages/sql/sqlite-react-native/src/index.ts index 986ce8399..210d7ee08 100644 --- a/repos/effect/packages/sql/sqlite-react-native/src/index.ts +++ b/repos/effect/packages/sql/sqlite-react-native/src/index.ts @@ -1,15 +1,18 @@ /** + * @stability unstable * @since 4.0.0 */ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 4.0.0 */ export * as SqliteClient from "./SqliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteMigrator from "./SqliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md b/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md index 188d11024..9353cdb39 100644 --- a/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md +++ b/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/sql-sqlite-wasm +## 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/sql/sqlite-wasm/package.json b/repos/effect/packages/sql/sqlite-wasm/package.json index 4e7742996..12464ad84 100644 --- a/repos/effect/packages/sql/sqlite-wasm/package.json +++ b/repos/effect/packages/sql/sqlite-wasm/package.json @@ -1,6 +1,6 @@ { "name": "@effect/sql-sqlite-wasm", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A SQLite toolkit for Effect", diff --git a/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts b/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts index f71deb48c..8f4454b94 100644 --- a/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts +++ b/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts @@ -8,6 +8,7 @@ * exports database bytes, forwards update-hook notifications, and closes when * requested. It is meant to run in a dedicated worker or a `SharedWorker`. * + * @stability unstable * @since 4.0.0 */ /// @@ -25,6 +26,7 @@ const classifyError = (cause: unknown, message: string, operation: string) => /** * Configuration for the SQLite OPFS worker, including the message port used for the client protocol and the OPFS database name to open. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -36,6 +38,7 @@ export interface OpfsWorkerConfig { /** * Runs the SQLite OPFS worker loop, opening the configured database, posting a ready message, handling query/import/export/update-hook messages, and closing when a close message is received. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts b/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts index 8146d339f..f375fc789 100644 --- a/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts +++ b/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts @@ -10,6 +10,7 @@ * clients can stream query rows; worker-backed clients cannot. `updateValues` * is not supported by this driver. * + * @stability unstable * @since 4.0.0 */ // oxlint-disable-next-line effect/no-import-from-barrel-package @@ -43,6 +44,7 @@ const classifyError = (cause: unknown, message: string, operation: string) => /** * Runtime identifier attached to SQLite WASM client values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -51,6 +53,7 @@ export const TypeId: TypeId = "~@effect/sql-sqlite-wasm/SqliteClient" /** * Type-level identifier for SQLite WASM client values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -59,6 +62,7 @@ export type TypeId = "~@effect/sql-sqlite-wasm/SqliteClient" /** * SQLite WASM client service interface, extending `SqlClient` with database `export` and `import` operations and marking `updateValues` as unsupported for SQLite. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -75,6 +79,7 @@ export interface SqliteClient extends Client.SqlClient { /** * Service tag for the SQLite WASM client. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -83,6 +88,7 @@ export const SqliteClient = Context.Service("@effect/sql-sqlite-wa /** * Configuration for an in-memory SQLite WASM client, including optional reactivity hooks, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -96,6 +102,7 @@ export interface SqliteClientMemoryConfig { /** * Configuration for a worker-backed SQLite WASM client, including the scoped worker or message port, optional reactivity hooks, span attributes, and query/result name transforms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -129,6 +136,7 @@ const registered = new Set() /** * Creates a scoped in-memory SQLite WASM client using the memory VFS, serializing access through a semaphore and exposing database `export` and `import` operations. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -290,6 +298,7 @@ export const makeMemory = ( /** * Creates a scoped worker-backed SQLite WASM client, communicating with the configured worker or message port, restarting the scoped connection on worker errors, and exposing database `export` and `import` operations. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -472,6 +481,7 @@ const extractRows = (rows: WorkerResult) => rows[1] /** * Fiber reference that stores transferables to include with worker-backed SQLite WASM query messages. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -483,6 +493,7 @@ export const Transferables = Context.Reference>( /** * Runs an effect with the supplied transferables attached to worker-backed SQLite WASM query messages. * + * @stability unstable * @category transferables * @since 4.0.0 */ @@ -493,6 +504,7 @@ export const withTransferables = /** * Builds a layer from an Effect `Config` value, providing both the in-memory SQLite WASM `SqliteClient` service and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -513,6 +525,7 @@ export const layerMemoryConfig = ( /** * Builds a layer from an in-memory SQLite WASM client configuration, providing both `SqliteClient` and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -529,6 +542,7 @@ export const layerMemory = ( /** * Builds a layer from a worker-backed SQLite WASM client configuration, providing both `SqliteClient` and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -545,6 +559,7 @@ export const layer = ( /** * Builds a layer from an Effect `Config` value, providing both the worker-backed SQLite WASM `SqliteClient` service and the generic `SqlClient` service. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts b/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts index b4ce0dc3f..70a843e7a 100644 --- a/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts +++ b/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts @@ -17,6 +17,7 @@ * browser- and origin-dependent, and this adapter does not currently write * SQLite schema dumps for `schemaDirectory`. * + * @stability unstable * @since 4.0.0 */ import type * as Effect from "effect/Effect" @@ -33,6 +34,7 @@ export * from "effect/sql/Migrator" /** * Runs SQL migrations for a SQLite WASM database using the shared `Migrator` implementation and the current `SqlClient`. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -47,6 +49,7 @@ export const run: ( /** * Creates a layer that runs the configured SQLite WASM migrations during layer construction and provides no services. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/sql/sqlite-wasm/src/index.ts b/repos/effect/packages/sql/sqlite-wasm/src/index.ts index 043b46bb0..2a2d1e1bb 100644 --- a/repos/effect/packages/sql/sqlite-wasm/src/index.ts +++ b/repos/effect/packages/sql/sqlite-wasm/src/index.ts @@ -1,14 +1,17 @@ /** + * @stability unstable * @since 4.0.0 */ export * as OpfsWorker from "./OpfsWorker.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteClient from "./SqliteClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as SqliteMigrator from "./SqliteMigrator.ts" diff --git a/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts b/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts index a1ac625c9..35c2c5c7e 100644 --- a/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts +++ b/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts @@ -1,6 +1,7 @@ /** * Declares the wa-sqlite IndexedDB batch-atomic VFS example module. * + * @stability unstable * @since 4.0.0 */ declare module "@effect/wa-sqlite/src/examples/IDBBatchAtomicVFS.js" { diff --git a/repos/effect/packages/tools/ai-codegen/package.json b/repos/effect/packages/tools/ai-codegen/package.json index 4d1ed95e7..bed301d5f 100644 --- a/repos/effect/packages/tools/ai-codegen/package.json +++ b/repos/effect/packages/tools/ai-codegen/package.json @@ -53,6 +53,6 @@ "yaml": "^2.9.1" }, "devDependencies": { - "@types/node": "^26.6.3" + "@types/node": "^26.6.4" } } diff --git a/repos/effect/packages/tools/ai-docgen/package.json b/repos/effect/packages/tools/ai-docgen/package.json index ebea3b45a..8c9521113 100644 --- a/repos/effect/packages/tools/ai-docgen/package.json +++ b/repos/effect/packages/tools/ai-docgen/package.json @@ -52,6 +52,6 @@ "yaml": "^2.9.1" }, "devDependencies": { - "@types/node": "^26.6.3" + "@types/node": "^26.6.4" } } diff --git a/repos/effect/packages/tools/api-diff/package.json b/repos/effect/packages/tools/api-diff/package.json index fecc1a1d5..5041f08ba 100644 --- a/repos/effect/packages/tools/api-diff/package.json +++ b/repos/effect/packages/tools/api-diff/package.json @@ -22,9 +22,9 @@ }, "devDependencies": { "@effect/vitest": "workspace:^", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "typescript": "^6.0.3", "typescript-compiler": "npm:typescript@6.0.3", - "vitest": "5.0.2" + "vitest": "5.0.3" } } diff --git a/repos/effect/packages/tools/bundle/package.json b/repos/effect/packages/tools/bundle/package.json index 9c755052b..6bfc567a4 100644 --- a/repos/effect/packages/tools/bundle/package.json +++ b/repos/effect/packages/tools/bundle/package.json @@ -41,13 +41,13 @@ "@rollup/plugin-terser": "^1.0.0", "effect": "workspace:^", "glob": "^13.0.6", - "rollup": "^4.63.5", + "rollup": "^4.64.0", "rollup-plugin-esbuild": "^6.2.1", "rollup-plugin-visualizer": "^7.1.1" }, "devDependencies": { "@babel/core": "^8.0.6", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "babel-plugin-annotate-pure-calls": "^0.5.0", "esbuild": "^0.28.2" } diff --git a/repos/effect/packages/tools/docgen/CHANGELOG.md b/repos/effect/packages/tools/docgen/CHANGELOG.md index 030c2a9d9..d3d12a617 100644 --- a/repos/effect/packages/tools/docgen/CHANGELOG.md +++ b/repos/effect/packages/tools/docgen/CHANGELOG.md @@ -1,5 +1,14 @@ # @effect/docgen +## 4.0.2 + +### Patch Changes + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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), [`48a373b`](https://github.com/Effect-TS/effect/commit/48a373b2b4fdacc7e65fd05e5260b7b1c2f09b6d), [`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 + - @effect/platform-node@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/tools/docgen/package.json b/repos/effect/packages/tools/docgen/package.json index bfcf4d530..f2c704238 100644 --- a/repos/effect/packages/tools/docgen/package.json +++ b/repos/effect/packages/tools/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@effect/docgen", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "An opinionated documentation generator for Effect projects", @@ -60,7 +60,7 @@ "@babel/code-frame": "^8.0.6", "@effect/markdown-toc": "^0.1.0", "@effect/platform-node": "workspace:^", - "chalk": "^6.0.0", + "chalk": "^6.0.1", "doctrine": "^3.0.0", "effect": "workspace:^", "glob": "^13.0.6", @@ -76,9 +76,9 @@ "@effect/vitest": "workspace:^", "@types/babel__code-frame": "^7.27.0", "@types/doctrine": "^0.0.9", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "tsx": "^4.23.15", "typescript": "^6.0.3", - "vitest": "^5.0.2" + "vitest": "^5.0.3" } } diff --git a/repos/effect/packages/tools/doctest/CHANGELOG.md b/repos/effect/packages/tools/doctest/CHANGELOG.md index ef551409c..634957ca6 100644 --- a/repos/effect/packages/tools/doctest/CHANGELOG.md +++ b/repos/effect/packages/tools/doctest/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/doctest +## 4.0.2 + +### Patch Changes + +- [#8740](https://github.com/Effect-TS/effect/pull/8740) [`ec54070`](https://github.com/Effect-TS/effect/commit/ec540703a59bf66a93f4329730b9be2343bea519) Thanks @tim-smart! - Update repository dependencies to their latest compatible 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/tools/doctest/package.json b/repos/effect/packages/tools/doctest/package.json index 4f4579cbf..481a422e3 100644 --- a/repos/effect/packages/tools/doctest/package.json +++ b/repos/effect/packages/tools/doctest/package.json @@ -1,6 +1,6 @@ { "name": "@effect/doctest", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "Runs TypeScript documentation examples as Vitest tests", @@ -46,7 +46,7 @@ "check": "tsc -b tsconfig.json" }, "dependencies": { - "rolldown": "^1.2.11" + "rolldown": "^1.2.12" }, "peerDependencies": { "effect": "workspace:^", @@ -55,9 +55,9 @@ }, "devDependencies": { "@effect/vitest": "workspace:^", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "effect": "workspace:^", - "vite": "^8.3.1", - "vitest": "^5.0.2" + "vite": "^8.3.2", + "vitest": "^5.0.3" } } diff --git a/repos/effect/packages/tools/jsdocs/package.json b/repos/effect/packages/tools/jsdocs/package.json index 56c2280a2..56a8ae5bd 100644 --- a/repos/effect/packages/tools/jsdocs/package.json +++ b/repos/effect/packages/tools/jsdocs/package.json @@ -54,8 +54,8 @@ }, "devDependencies": { "@effect/vitest": "workspace:^", - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "typescript": "^6.0.3", - "vitest": "^5.0.2" + "vitest": "^5.0.3" } } diff --git a/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts b/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts index d23353b03..9f3512305 100644 --- a/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts +++ b/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts @@ -791,7 +791,9 @@ function resolveBarrelImport( function resolveJSDocImports( cwd: string, - filename: string + filename: string, + sourceFile: ts.SourceFile, + checker: ts.TypeChecker ): Result { const absoluteFilename = path.resolve(filename) const packageRoot = findPackageRoot(absoluteFilename) @@ -810,20 +812,17 @@ function resolveJSDocImports( error: `${relativeFilename} is not under ${normalizePathName(path.relative(cwd, metadata.value.sourceRoot))}` } } - if (path.basename(absoluteFilename) === "index.ts") { - return { - _tag: "Failure", - error: `${relativeFilename} is a barrel file; exclude it from jsdocs` - } - } if (path.extname(absoluteFilename) !== ".ts" || absoluteFilename.endsWith(".d.ts")) { return { _tag: "Failure", error: `${relativeFilename} is not a TypeScript source module` } } - const moduleSubpath = relativeToSourceRoot.slice(0, -".ts".length) - const moduleExportSubpath = `./${moduleSubpath}` + const isIndex = path.basename(absoluteFilename) === "index.ts" + const moduleSubpath = isIndex + ? relativeToSourceRoot.slice(0, -"index.ts".length).replace(/\/$/, "") + : relativeToSourceRoot.slice(0, -".ts".length) + const moduleExportSubpath = moduleSubpath === "" ? "." : `./${moduleSubpath}` const moduleExportTarget = `./src/${relativeToSourceRoot}` if (!isPackageSubpathExported(metadata.value.exports, moduleExportSubpath, moduleExportTarget)) { return { @@ -833,7 +832,16 @@ function resolveJSDocImports( } for ${relativeFilename}` } } - const barrel = resolveBarrelImport(metadata.value, absoluteFilename) + const symbol = checker.getSymbolAtLocation(sourceFile) + const barrel = isIndex + ? { + _tag: "Success", + value: { + barrel: null, + flatNames: symbol === undefined ? [] : checker.getExportsOfModule(symbol).map((item) => item.name) + } + } as const + : resolveBarrelImport(metadata.value, absoluteFilename) if (barrel._tag === "Failure") { return barrel } @@ -1470,7 +1478,8 @@ function extractParsedInlineLinks(source: string): ReadonlyArray + tags: ReadonlyArray, + requireStability = false ): Result { const diagnostics: Array = [] const allowed = scope === "declaration" @@ -1527,8 +1536,18 @@ function buildTags( } const deprecated = values.get("deprecated")?.[0] ?? null const stability = values.get("stability")?.[0] - if (stability !== undefined && stability !== "unstable" && stability !== "experimental") { - diagnostics.push(diagnostic("invalid-stability", "@stability must have the value unstable or experimental")) + if (stability !== undefined && stability !== "stable" && stability !== "unstable" && stability !== "experimental") { + diagnostics.push( + diagnostic("invalid-stability", "@stability must have the value stable, unstable, or experimental") + ) + } + if (requireStability && stability === undefined) { + diagnostics.push( + diagnostic( + "missing-tag", + scope === "module" ? "Module JSDoc must include @stability" : "Public JSDoc must include @stability" + ) + ) } const resolvedStability: JSDocStability = stability === "unstable" || stability === "experimental" ? stability @@ -2546,8 +2565,9 @@ function createExampleImportPolicy(files: ReadonlyArray): Exampl }) continue } - for (const name of rootValues) addAllowed(file.imports.module, name) - namedModuleBySource.set(file.imports.module, new Set(rootValues)) + const names = new Set([...rootValues, ...file.imports.flatNames]) + for (const name of names) addAllowed(file.imports.module, name) + namedModuleBySource.set(file.imports.module, names) } return { allowedNamedBySource: allowedNamed, namespaceModuleBySource, flatModuleBySource, namedModuleBySource } } @@ -2746,7 +2766,21 @@ function parseDocumentedTs( required = true, linkContext?: { readonly checker: ts.TypeChecker; readonly entry: ProgramCacheEntry; readonly cwd: string } ) { - const block = getNodeJSDoc(node) + let block = getNodeJSDoc(node) + // Barrel re-exports intentionally use minimal, tag-only JSDoc. + if (block !== undefined && (ts.isExportDeclaration(node) || ts.isExportSpecifier(node))) { + const loose = parseLooseJSDocBlock(block.raw, block.range) + if (loose.tags[0]?.line === 0) { + block = { + ...loose, + parsed: { + description: { short: "", whenToUse: null, details: null, gotchas: null }, + examples: [], + tags: loose.tags + } + } + } + } if (block?.internal) return undefined if (block === undefined) { if (required) { @@ -2759,7 +2793,9 @@ function parseDocumentedTs( } addModelDiagnostics(diagnostics, block.range, block.diagnostics) if (block.parsed === undefined) return undefined - const tags = buildTags(scope, block.parsed.tags) + // Only names that can be imported directly require @stability. + const requireStability = scope === "declaration" || (scope === "namespace" && ts.isSourceFile(node.parent)) + const tags = buildTags(scope, block.parsed.tags, requireStability) if (tags._tag === "Failure") { addModelDiagnostics(diagnostics, block.range, tags.error.diagnostics) return undefined @@ -3282,7 +3318,7 @@ function parseModuleJSDocTs( ): ParsedModuleJSDoc | undefined { const block = parseLooseJSDocBlock(moduleJSDoc.raw, [moduleJSDoc.range[0], moduleJSDoc.range[1]]) if (block.internal) return undefined - const tags = buildTags("module", block.tags) + const tags = buildTags("module", block.tags, true) if (tags._tag === "Failure") addModelDiagnostics(diagnostics, block.range, tags.error.diagnostics) const examples = parseModuleExamples(block) addModelDiagnostics(diagnostics, block.range, examples.diagnostics) @@ -3310,6 +3346,22 @@ function parseSourceFileDocs( if (ts.isExportAssignment(statement)) continue if (ts.isExportDeclaration(statement)) { if (statement.exportClause === undefined) continue + if (ts.isNamespaceExport(statement.exportClause)) { + const documented = parseDocumentedTs(statement, "namespace", diagnostics, true, linkContext) + if (documented !== undefined) { + namespaces.push({ + name: statement.exportClause.name.text, + signature: statement.getText(sourceFile), + range: nodeRange(statement), + description: documented.core.description, + examples: documented.core.examples, + tags: documented.tags as ParsedNamespaceTags, + declarations: [], + namespaces: [] + }) + } + continue + } if (ts.isNamedExports(statement.exportClause) && statement.exportClause.elements.length === 0) { diagnostics.push({ ...diagnostic("empty-export", "Empty export declarations are not allowed"), @@ -3458,7 +3510,7 @@ export function extractJSDocsSync(options: ExtractJSDocsOptions): JSDocModel { result.parsed.declarations.length > 0 || result.parsed.namespaces.length > 0 || result.parsed.moduleJSDoc !== undefined ) { - const imports = resolveJSDocImports(cwd, filename) + const imports = resolveJSDocImports(cwd, filename, sourceFile, checker) if (imports._tag === "Success") importsValue = imports.value else { fileDiagnostics.push({ diff --git a/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts b/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts index 1a128d899..494bb8695 100644 --- a/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts +++ b/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts @@ -1,4 +1,4 @@ -import { computeJSDocInputHash, extractJSDocsSync, parseJSDoc } from "@effect/jsdocs" +import { computeJSDocInputHash, extractJSDocsSync, loadJSDocConfig, parseJSDoc } from "@effect/jsdocs" import { assert, describe, it } from "@effect/vitest" import * as fs from "node:fs" import * as os from "node:os" @@ -28,7 +28,14 @@ const stabilityResult = (tag: string) => { exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), "export * as Foo from \"./Foo.ts\"\n") + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** @@ -56,6 +63,48 @@ export const makeValue = () => 1 } } +const missingStabilityDiagnostics = (files: Record, moduleStability = "@stability stable") => { + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "jsdocs-require-stability-")) + try { + fs.mkdirSync(path.join(cwd, "src")) + fs.writeFileSync( + path.join(cwd, "tsconfig.json"), + JSON.stringify({ + compilerOptions: { module: "NodeNext", moduleResolution: "NodeNext", target: "ES2022" }, + include: ["src/**/*.ts"] + }) + ) + fs.writeFileSync( + path.join(cwd, "package.json"), + JSON.stringify({ name: "@effect/sample", type: "module", exports: { "./*": "./src/*.ts" } }) + ) + for (const [file, source] of Object.entries(files)) { + fs.writeFileSync( + path.join(cwd, "src", file), + `/** + * Module. + * + * ${moduleStability} + * @since 1.0.0 + */ +import type {} from "node:fs" + +${source}` + ) + } + const model = extractJSDocsSync({ cwd, tsconfig: "tsconfig.json", include: ["src/**/*.ts"], output: "out.json" }) + return Object.fromEntries( + Object.keys(files).map((file) => [ + file, + model.files.find((modelFile) => modelFile.file === `src/${file}`)?.diagnostics.map((item) => item.message) ?? + [] + ]) + ) + } finally { + fs.rmSync(cwd, { recursive: true, force: true }) + } +} + const signatureParseDiagnostics = (signature: string): ReadonlyArray => (ts.createSourceFile( "signature.ts", @@ -98,9 +147,170 @@ describe("jsdocs", () => { }) }) + it("accepts explicit stable declarations", () => { + assert.deepStrictEqual(stabilityResult("@stability stable"), { + diagnostics: [], + stability: "stable" + }) + }) + it("rejects unknown stability values", () => { assert.deepStrictEqual(stabilityResult("@stability bogus").diagnostics, ["invalid-stability"]) - assert.deepStrictEqual(stabilityResult("@stability stable").diagnostics, ["invalid-stability"]) + }) + + it("requires @stability on directly importable declarations", () => { + assert.deepStrictEqual( + missingStabilityDiagnostics({ + "Declaration.ts": `/** + * A value. + * + * @category constructors + * @since 1.0.0 + */ +export const value = 1 +`, + "Specifier.ts": `const value = 1 + +export { + /** + * A value. + * + * @category constructors + * @since 1.0.0 + */ + value +} +`, + "Namespace.ts": `/** + * A group. + * + * @category models + * @since 1.0.0 + */ +export declare namespace Group {} +`, + "Nested.ts": `/** + * A group. + * + * @stability stable + * @category models + * @since 1.0.0 + */ +export declare namespace Group { + /** + * Declarations inside a namespace do not require a stability tag. + * + * @category models + * @since 1.0.0 + */ + export interface Item { + /** + * Members do not require a stability tag. + */ + readonly id: string + } +} +` + }), + { + "Declaration.ts": ["Public JSDoc must include @stability"], + "Specifier.ts": ["Public JSDoc must include @stability"], + "Namespace.ts": ["Public JSDoc must include @stability"], + "Nested.ts": [] + } + ) + }) + + it("requires @stability on module JSDoc", () => { + assert.deepStrictEqual(missingStabilityDiagnostics({ "Module.ts": "" }, "Has no stability tag."), { + "Module.ts": ["Module JSDoc must include @stability"] + }) + }) + + it("requires @stability on namespace re-exports", () => { + assert.deepStrictEqual( + missingStabilityDiagnostics({ + "Missing.ts": `/** + * Re-exports a module. + * + * @category exports + * @since 1.0.0 + */ +export * as Target from "./Target.ts" +`, + "Tagged.ts": `/** + * Re-exports a module. + * + * @stability stable + * @category exports + * @since 1.0.0 + */ +export * as Target from "./Target.ts" +`, + "Target.ts": `/** + * A value. + * + * @stability stable + * @category constructors + * @since 1.0.0 + */ +export const value = 1 +` + }), + { + "Missing.ts": ["Public JSDoc must include @stability"], + "Tagged.ts": [], + "Target.ts": [] + } + ) + }) + + it("checks hand-maintained public index exports with the repository JSDoc config", () => { + const config = loadJSDocConfig(path.resolve(import.meta.dirname, "../../../..")) + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "jsdocs-public-index-")) + try { + const packageRoot = path.join(cwd, "packages/atom/vue") + fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true }) + fs.writeFileSync( + path.join(cwd, config.tsconfig), + JSON.stringify({ + compilerOptions: { module: "NodeNext", moduleResolution: "NodeNext", target: "ES2022" }, + include: ["packages/**/src/**/*.ts"] + }) + ) + fs.writeFileSync( + path.join(packageRoot, "package.json"), + JSON.stringify({ name: "@effect/atom-vue", type: "module", exports: { ".": "./src/index.ts" } }) + ) + fs.writeFileSync( + path.join(packageRoot, "src/index.ts"), + `/** + * Public entry point. + * + * @stability unstable + * @since 1.0.0 + */ +import type {} from "node:fs" + +/** + * A directly importable value. + * + * @category constructors + * @since 1.0.0 + */ +export const value = 1 +` + ) + const model = extractJSDocsSync({ ...config, cwd }) + assert.deepStrictEqual( + model.files.find((file) => file.file === "packages/atom/vue/src/index.ts")?.diagnostics + .filter((diagnostic) => diagnostic.code === "missing-tag") + .map((diagnostic) => diagnostic.message), + ["Public JSDoc must include @stability"] + ) + } finally { + fs.rmSync(cwd, { recursive: true, force: true }) + } }) it("rejects duplicate stability tags", () => { @@ -110,7 +320,7 @@ describe("jsdocs", () => { }) it("rejects legacy unstable tags", () => { - assert.deepStrictEqual(stabilityResult("@unstable").diagnostics, ["forbidden-tag"]) + assert.deepStrictEqual(stabilityResult("@unstable").diagnostics, ["forbidden-tag", "missing-tag"]) }) it("accepts doctest metadata on TypeScript fences", () => { @@ -187,12 +397,20 @@ describe("jsdocs", () => { exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Creates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -207,7 +425,11 @@ export const makeValue = () => 1 } const model = extractJSDocsSync(options) assert.strictEqual(model.version, 3) - assert.strictEqual(model.files.length, 1) + assert.deepStrictEqual(model.files.map((file) => file.file), ["src/Foo.ts", "src/index.ts"]) + const index = model.files.find((file) => file.file === "src/index.ts") + assert.deepStrictEqual(index?.diagnostics, []) + assert.strictEqual(index?.namespaces[0]?.name, "Foo") + assert.strictEqual(model.apis.some((api) => api.apiFqn === "@effect/sample.Foo"), true) assert.strictEqual(model.files[0]?.declarations[0]?.name, "makeValue") assert.strictEqual(model.apis[0]?.apiFqn, "@effect/sample/Foo.makeValue") assert.deepStrictEqual(model.apis[0]?.importGuidance, { @@ -248,12 +470,20 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Creates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -293,12 +523,20 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/External.ts"), `/** * External value type. * + * @stability stable * @category models * @since 1.0.0 */ @@ -309,6 +547,7 @@ export interface External { /** * Makes an external value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -320,6 +559,7 @@ export const makeExternal = (value: string): External => ({ value }) `/** * Parses a value. * + * @stability stable * @category parsing * @since 1.0.0 */ @@ -332,6 +572,7 @@ export function parse(value: string | number) { /** * Converts a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -343,6 +584,7 @@ export { /** * Negates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -353,6 +595,7 @@ export { /** * Makes an external value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -365,6 +608,7 @@ export { /** * Attempts a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -374,6 +618,7 @@ export { /** * Handles many overloads. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -397,6 +642,7 @@ export function many(value: number) { /** * Runs a value through a transform chain. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -468,6 +714,7 @@ interface Ctor> { /** * Builds a boxed constructor. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -480,6 +727,7 @@ export function boxed>( /** * Handles distinct overload modes. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -500,6 +748,7 @@ export function modes(...args: Array) { /** * Service for sample values. * + * @stability stable * @category services * @since 1.0.0 */ @@ -516,6 +765,7 @@ export class Service { /** * Default count. * + * @stability stable * @category constants * @since 1.0.0 */ @@ -524,6 +774,7 @@ export const count = 1 /** * Sample value type. * + * @stability stable * @category models * @since 1.0.0 */ @@ -608,7 +859,14 @@ export { _try as try }` exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** @@ -624,6 +882,7 @@ export { _try as try }` * \`\`\` * * @see {@link makeValue} + * @stability stable * @since 1.0.0 */ import type { Buffer } from "node:buffer" @@ -631,6 +890,7 @@ import type { Buffer } from "node:buffer" /** * Creates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -665,12 +925,20 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Creates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -705,13 +973,21 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync(path.join(cwd, "src/Schema.ts"), `export {}\n`) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Creates a value with the {@link Schema} module. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -752,7 +1028,14 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** @@ -763,6 +1046,7 @@ export const makeValue = () => 1 * \`\`\` * * @see {@link Hidden} + * @stability stable */ import type { Buffer } from "node:buffer" @@ -771,6 +1055,7 @@ class Hidden {} /** * Creates a value. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -808,12 +1093,20 @@ export const makeValue = () => 1 exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * A documented container. * + * @stability stable * @category models * @since 1.0.0 */ @@ -826,6 +1119,7 @@ export interface Box { * Uses the hidden member. * * @see {@link Box.hidden} + * @stability stable * @category constructors * @since 1.0.0 */ @@ -869,13 +1163,26 @@ export const useHidden = () => undefined ) fs.writeFileSync( path.join(cwd, "src/index.ts"), - `export * as Eq from "./Eq.ts"\nexport * as Ordering from "./Ordering.ts"\nexport * as Reducer from "./Reducer.ts"\n` + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Eq from "./Eq.ts"\n/** + * @stability stable + * @since 1.0.0 + */ +export * as Ordering from "./Ordering.ts"\n/** + * @stability stable + * @since 1.0.0 + */ +export * as Reducer from "./Reducer.ts"\n` ) fs.writeFileSync( path.join(cwd, "src/Reducer.ts"), `/** * A reusable reducer. * + * @stability stable * @category models * @since 1.0.0 */ @@ -892,6 +1199,7 @@ export interface Reducer { `/** * Ordering reducer value. * + * @stability stable * @category constants * @since 1.0.0 */ @@ -906,6 +1214,7 @@ export const Reducer = "ordering" * Creates an equivalence reducer. * * @see {@link Reducer} + * @stability stable * @category constructors * @since 1.0.0 */ @@ -946,12 +1255,20 @@ export const makeReducer = () => Reducer exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Private helper. * + * @stability stable * @category models * @since 1.0.0 */ @@ -961,6 +1278,7 @@ class Hidden {} * Uses a hidden helper. * * @see {@link Hidden} + * @stability stable * @category constructors * @since 1.0.0 */ @@ -1005,13 +1323,22 @@ export const useHidden = () => Hidden ) fs.writeFileSync( path.join(cwd, "src/index.ts"), - `export * as Bar from "./Bar.ts"\nexport * as Foo from "./Foo.ts"\n` + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Bar from "./Bar.ts"\n/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** * Target value. * + * @stability stable * @category constants * @since 1.0.0 */ @@ -1026,6 +1353,7 @@ export const Target = "target" * const value = "broken" * \`\`\` * + * @stability stable * @category constants * @since 1.0.0 */ @@ -1040,6 +1368,7 @@ export const Broken = "broken" * Uses the target value. * * @see {@link Foo.Target} for the target value + * @stability stable * @category constants * @since 1.0.0 */ @@ -1083,10 +1412,18 @@ export const useTarget = () => Foo.Target exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Foo.ts"), `/** + * @stability stable * @since 1.0.0 */ const Array_ = "array" @@ -1095,6 +1432,7 @@ export { /** * Public array helper. * + * @stability stable * @category constructors * @since 1.0.0 */ @@ -1106,6 +1444,7 @@ export { * * @see {@link Array_} * @see {@link Array} + * @stability stable * @category constructors * @since 1.0.0 */ @@ -1150,12 +1489,20 @@ export const Tuple = Array exports: { ".": "./src/index.ts", "./*": "./src/*.ts" } }) ) - fs.writeFileSync(path.join(cwd, "src/index.ts"), `export * as Foo from "./Foo.ts"\n`) + fs.writeFileSync( + path.join(cwd, "src/index.ts"), + `/** + * @stability stable + * @since 1.0.0 + */ +export * as Foo from "./Foo.ts"\n` + ) fs.writeFileSync( path.join(cwd, "src/Imported.ts"), `/** * Imported marker. * + * @stability stable * @category models * @since 1.0.0 */ @@ -1177,6 +1524,7 @@ import type { Marker } from "./Imported.ts" /** * A boxed value. * + * @stability stable * @category models * @since 1.0.0 */ @@ -1230,6 +1578,7 @@ export declare namespace Group { /** * Groups stable types. * + * @stability stable * @category models * @since 1.0.0 */ diff --git a/repos/effect/packages/tools/openapi-generator/CHANGELOG.md b/repos/effect/packages/tools/openapi-generator/CHANGELOG.md index 9ec684fcd..7ae912f7b 100644 --- a/repos/effect/packages/tools/openapi-generator/CHANGELOG.md +++ b/repos/effect/packages/tools/openapi-generator/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/openapi-generator +## 4.0.2 + +### Patch Changes + +- 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), [`48a373b`](https://github.com/Effect-TS/effect/commit/48a373b2b4fdacc7e65fd05e5260b7b1c2f09b6d), [`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 + - @effect/platform-node@4.0.2 + ## 4.0.1 ### Patch Changes diff --git a/repos/effect/packages/tools/openapi-generator/package.json b/repos/effect/packages/tools/openapi-generator/package.json index 7d8cc598b..ee1c324a9 100644 --- a/repos/effect/packages/tools/openapi-generator/package.json +++ b/repos/effect/packages/tools/openapi-generator/package.json @@ -1,7 +1,7 @@ { "name": "@effect/openapi-generator", "type": "module", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "description": "Generate Effect Schema types, HTTP clients, and HttpApi modules from OpenAPI specifications", "homepage": "https://effect.website", @@ -73,6 +73,6 @@ "effect": "workspace:^", "json-schema-typed": "^8.0.2", "openapi-typescript": "^7.13.0", - "rolldown": "^1.2.11" + "rolldown": "^1.2.12" } } diff --git a/repos/effect/packages/tools/oxc/package.json b/repos/effect/packages/tools/oxc/package.json index c69791d87..24faae194 100644 --- a/repos/effect/packages/tools/oxc/package.json +++ b/repos/effect/packages/tools/oxc/package.json @@ -56,14 +56,14 @@ "check": "tsc -b tsconfig.json" }, "dependencies": { - "@oxlint/plugins": "^1.85.0" + "@oxlint/plugins": "^1.86.0" }, "peerDependencies": { "typescript": ">=5.0.0 <7.0.0" }, "devDependencies": { - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "typescript": "^6.0.3", - "vitest": "^5.0.2" + "vitest": "^5.0.3" } } diff --git a/repos/effect/packages/tools/utils/package.json b/repos/effect/packages/tools/utils/package.json index 70e0c7eb3..514bec5f9 100644 --- a/repos/effect/packages/tools/utils/package.json +++ b/repos/effect/packages/tools/utils/package.json @@ -51,6 +51,6 @@ "glob": "^13.0.6" }, "devDependencies": { - "@types/node": "^26.6.3" + "@types/node": "^26.6.4" } } diff --git a/repos/effect/packages/tools/utils/src/Codegen.ts b/repos/effect/packages/tools/utils/src/Codegen.ts index b384e2e2e..d93566a76 100644 --- a/repos/effect/packages/tools/utils/src/Codegen.ts +++ b/repos/effect/packages/tools/utils/src/Codegen.ts @@ -68,7 +68,7 @@ const parseAnnotation = (line: string): string | undefined => { interface ModuleMetadata { readonly since: string - readonly stability: "unstable" | "experimental" | undefined + readonly stability: "stable" | "unstable" | "experimental" | undefined } const extractModuleMetadata = (file: string, content: string): Effect.Effect => { @@ -105,7 +105,8 @@ const extractModuleMetadata = (file: string, content: string): Effect.Effect { * @since 3.0.0 */ export const experimental = 1 +` + ) + yield* fs.writeFileString( + path.join(directory, "ExplicitStable.ts"), + `/** + * Explicitly stable module. + * + * @stability stable + * @since 1.0.0 + */ +export const explicitStable = 1 ` ) yield* fs.writeFileString( @@ -75,6 +86,12 @@ export const unstable = 1 */ export * as Experimental from "./Experimental.ts" +/** + * @stability stable + * @since 1.0.0 + */ +export * as ExplicitStable from "./ExplicitStable.ts" + /** * @since 1.0.0 */ diff --git a/repos/effect/packages/vitest/CHANGELOG.md b/repos/effect/packages/vitest/CHANGELOG.md index 4bb7d27e7..ff686c920 100644 --- a/repos/effect/packages/vitest/CHANGELOG.md +++ b/repos/effect/packages/vitest/CHANGELOG.md @@ -1,5 +1,13 @@ # @effect/vitest +## 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/vitest/package.json b/repos/effect/packages/vitest/package.json index 5eacde243..4ac2a5992 100644 --- a/repos/effect/packages/vitest/package.json +++ b/repos/effect/packages/vitest/package.json @@ -1,6 +1,6 @@ { "name": "@effect/vitest", - "version": "4.0.1", + "version": "4.0.2", "type": "module", "license": "MIT", "description": "A set of helpers for testing Effects with vitest", @@ -54,8 +54,8 @@ "vitest": ">=5.0.0 <6.0.0" }, "devDependencies": { - "@types/node": "^26.6.3", + "@types/node": "^26.6.4", "effect": "workspace:^", - "vitest": "5.0.2" + "vitest": "5.0.3" } } diff --git a/repos/effect/packages/vitest/src/index.ts b/repos/effect/packages/vitest/src/index.ts index 20c0497eb..9d65132e1 100644 --- a/repos/effect/packages/vitest/src/index.ts +++ b/repos/effect/packages/vitest/src/index.ts @@ -1,4 +1,7 @@ /** + * Vitest integration for scoped Effect tests, shared layers, and property tests. + * + * @stability unstable * @since 4.0.0 */ import type * as Arbitrary from "effect/Arbitrary" @@ -19,16 +22,24 @@ import * as internal from "./internal/internal.ts" export * from "vitest" /** + * Vitest test API without additional fixtures. + * * @stability unstable + * @category models * @since 4.0.0 */ export type API = V.TestAPI<{}> /** + * Types for Effect-aware Vitest tests and helpers. + * + * @stability unstable * @since 4.0.0 */ -export namespace Vitest { +export declare namespace Vitest { /** + * An Effect-returning test callback with the specified arguments. + * * @since 4.0.0 */ export interface TestFunction> { @@ -36,6 +47,8 @@ export namespace Vitest { } /** + * Registers an Effect test with a name and optional Vitest options. + * * @since 4.0.0 */ export interface Test { @@ -47,6 +60,8 @@ export namespace Vitest { } /** + * Schema or Arbitrary inputs for tuple-based or keyed property tests. + * * @since 4.0.0 */ export type Arbitraries = @@ -58,6 +73,8 @@ export namespace Vitest { : never /** + * Effect test registration API with modifiers and property testing. + * * @since 4.0.0 */ export interface Tester extends Vitest.Test { @@ -115,6 +132,8 @@ export namespace Vitest { } /** + * Vitest test helpers with test services and shared layers. + * * @since 4.0.0 */ export interface MethodsNonLive extends V.TestAPI { @@ -168,6 +187,8 @@ export namespace Vitest { } /** + * Vitest test helpers including live-service tests and shared layers. + * * @since 4.0.0 */ export interface Methods extends MethodsNonLive { @@ -188,16 +209,28 @@ export namespace Vitest { } /** + * Invokes the Effect Vitest equality-tester registration hook. + * + * @stability unstable + * @category utils * @since 4.0.0 */ export const addEqualityTesters: () => void = internal.addEqualityTesters /** + * Runs a scoped Effect test with test clock and console services. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const effect: Vitest.Tester = internal.effect /** + * Runs a scoped Effect test with live services. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const live: Vitest.Tester = internal.live @@ -206,11 +239,13 @@ export const live: Vitest.Tester = internal.live * Share a `Layer` between multiple tests, optionally wrapping * the tests in a `describe` block if a name is provided. * + * **Details** + * * Named layers accept `concurrent` to override inherited suite concurrency. * Anonymous layers always inherit the enclosing suite's concurrency. * Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts. * - * @since 4.0.0 + * **Example** (Sharing layers between tests) * * ```ts * import { assert, layer } from "@effect/vitest" @@ -245,6 +280,10 @@ export const live: Vitest.Tester = internal.live * }) * }) * ``` + * + * @stability unstable + * @category testing + * @since 4.0.0 */ export const layer: ( layer_: Layer.Layer, @@ -260,6 +299,10 @@ export const layer: ( } = internal.layer /** + * Retries a scoped Effect to tolerate intermittent test failures, dying on exhausted retries. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const flakyTest: ( @@ -268,15 +311,19 @@ export const flakyTest: ( ) => Effect.Effect = internal.flakyTest /** + * Runs a synchronous property test using schemas or arbitraries. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const prop: Vitest.Methods["prop"] = internal.prop /** - * @since 4.0.0 - */ - -/** + * Vitest test API extended with Effect test helpers. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const it: Vitest.Methods = internal.it @@ -312,12 +359,18 @@ export const it: Vitest.Methods = internal.it * })) * ``` * + * @stability unstable + * @category constructors * @since 4.0.0 */ export const makeMethods: (it: V.TestAPI) => Vitest.Methods = internal.makeMethods /** + * Creates a named Vitest suite whose callback receives Effect test helpers. + * + * @stability unstable + * @category testing * @since 4.0.0 */ export const describeWrapped: (name: string, f: (it: Vitest.Methods) => void) => V.SuiteCollector = diff --git a/repos/effect/packages/vitest/src/utils.ts b/repos/effect/packages/vitest/src/utils.ts index 8ce34094b..4473632cf 100644 --- a/repos/effect/packages/vitest/src/utils.ts +++ b/repos/effect/packages/vitest/src/utils.ts @@ -7,6 +7,7 @@ * expressions, class instances, `Option`, `Result`, and `Exit`. Most helpers are * synchronous; `throwsAsync` handles rejected promises. * + * @stability unstable * @since 4.0.0 */ import type * as Cause from "effect/Cause" @@ -25,6 +26,7 @@ import { assert as vassert } from "vitest" /** * Fails the current test with the provided error message. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -35,6 +37,7 @@ export function fail(message: string) { /** * Asserts that `actual` is deeply strictly equal to `expected` using Node's `assert.deepStrictEqual`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -49,6 +52,7 @@ export function deepStrictEqual(actual: A, expected: A, message?: string, ... /** * Asserts that `actual` is not deeply strictly equal to `expected` using Node's `assert.notDeepStrictEqual`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -63,6 +67,7 @@ export function notDeepStrictEqual(actual: A, expected: A, message?: string, /** * Asserts that `actual` is strictly equal to `expected` using Node's `assert.strictEqual`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -77,6 +82,7 @@ export function strictEqual(actual: A, expected: A, message?: string, ..._: A /** * Asserts that `actual` is equal to `expected` using the `Equal.equals` trait. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -90,6 +96,7 @@ export function assertEquals(actual: A, expected: A, message?: string, ..._: /** * Asserts that `thunk` does not throw an error. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -104,6 +111,7 @@ export function doesNotThrow(thunk: () => void, message?: string, ..._: Array any>( /** * Asserts that `self` is `true`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -129,6 +138,7 @@ export function assertTrue(self: unknown, message?: string, ..._: Array): /** * Asserts that `self` is `false`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -139,6 +149,7 @@ export function assertFalse(self: boolean, message?: string, ..._: Array) /** * Asserts that `actual` includes `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -153,6 +164,7 @@ export function assertInclude(actual: string | undefined, expected: string, ..._ /** * Asserts that `actual` matches `regExp`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -165,6 +177,7 @@ export function assertMatch(actual: string, regExp: RegExp, ..._: Array) /** * Asserts that `thunk` throws, optionally checking the thrown value against an expected `Error` or validation function. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -189,6 +202,7 @@ export function throws(thunk: () => void, error?: Error | ((u: unknown) => undef /** * Asserts that `thunk` throws or returns a rejected promise, optionally checking the failure value against an expected `Error` or validation function. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -219,6 +233,7 @@ export async function throwsAsync( /** * Asserts that `option` is `None`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -229,6 +244,7 @@ export function assertNone(option: Option.Option, ..._: Array): ass /** * Asserts that `a` is not `undefined`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -244,6 +260,7 @@ export function assertDefined( /** * Asserts that `a` is `undefined`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -259,6 +276,7 @@ export function assertUndefined( /** * Asserts that `option` is `Some` and contains a value equal to `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -277,6 +295,7 @@ export function assertSome( /** * Asserts that `result` is `Success` and contains a value equal to `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -291,6 +310,7 @@ export function assertSuccess( /** * Asserts that `result` is `Failure` and contains an error equal to `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -309,6 +329,7 @@ export function assertFailure( /** * Asserts that `exit` is a failure with a cause equal to `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -323,6 +344,7 @@ export function assertExitFailure( /** * Asserts that `exit` is a success with a value equal to `expected`. * + * @stability unstable * @category testing * @since 4.0.0 */ diff --git a/repos/effect/packages/vitest/test/timeout.test.ts b/repos/effect/packages/vitest/test/timeout.test.ts index 5a49a80c6..0e844299b 100644 --- a/repos/effect/packages/vitest/test/timeout.test.ts +++ b/repos/effect/packages/vitest/test/timeout.test.ts @@ -51,15 +51,28 @@ describe("it.effect timeout retry finalizers", { concurrent: false }, () => { const abortedAtStart: Array = [] let attempts = 0 - it.effect.fails("retries after timing out while holding a resource", (ctx) => - Effect.gen(function*() { - const attempt = ++attempts - abortedAtStart.push(ctx.signal.aborted) - yield* resource(events, `attempt${attempt}`) + it.effect("retries after timing out while holding a resource", (ctx) => { + const attempt = ++attempts + abortedAtStart.push(ctx.signal.aborted) + if (attempt === 2) { + // The retry inherits an aborted signal. Keep its assertion uninterruptible. + return Effect.sync(() => { + events.push("retry:start") + assert.deepStrictEqual(events, [ + "attempt1:acquired", + "attempt1:release:start", + "attempt1:release:end", + "retry:start" + ]) + }).pipe(Effect.uninterruptible) + } + return Effect.gen(function*() { + yield* resource(events, "attempt1") return yield* Effect.never - }), { timeout: 75, retry: 1 }) + }) + }, { timeout: 75, retry: 1 }) - it("awaits the retry's finalizer before starting the next test", () => { + it("completes a genuine retry before starting the next test", () => { events.push("next:start") assert.strictEqual(attempts, 2) // Vitest reuses the signal aborted by the first attempt's timeout. @@ -68,9 +81,7 @@ describe("it.effect timeout retry finalizers", { concurrent: false }, () => { "attempt1:acquired", "attempt1:release:start", "attempt1:release:end", - "attempt2:acquired", - "attempt2:release:start", - "attempt2:release:end", + "retry:start", "next:start" ]) }) diff --git a/repos/effect/pnpm-lock.yaml b/repos/effect/pnpm-lock.yaml index 21e30ff11..9d9061e74 100644 --- a/repos/effect/pnpm-lock.yaml +++ b/repos/effect/pnpm-lock.yaml @@ -51,8 +51,8 @@ importers: specifier: workspace:^ version: link:packages/tools/oxc '@effect/tsgo': - specifier: ^0.46.1 - version: 0.46.1 + specifier: ^0.51.0 + version: 0.51.0 '@effect/utils': specifier: workspace:^ version: link:packages/tools/utils @@ -64,25 +64,25 @@ importers: version: 10.6.0 '@rollup/plugin-node-resolve': specifier: ^16.0.3 - version: 16.0.3(rollup@4.63.5) + version: 16.0.3(rollup@4.64.0) '@rollup/plugin-replace': specifier: ^6.0.3 - version: 6.0.3(rollup@4.63.5) + version: 6.0.3(rollup@4.64.0) '@rollup/plugin-terser': specifier: ^1.0.0 - version: 1.0.0(rollup@4.63.5) + version: 1.0.0(rollup@4.64.0) '@types/jscodeshift': specifier: ^17.3.0 version: 17.3.0 '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 '@vitest/coverage-v8': - specifier: ^5.0.2 - version: 5.0.2(vitest@5.0.2) + specifier: ^5.0.3 + version: 5.0.3(vitest@5.0.3) '@vitest/web-worker': - specifier: ^5.0.2 - version: 5.0.2(vitest@5.0.2) + specifier: ^5.0.3 + version: 5.0.3(vitest@5.0.3) ast-types: specifier: ^0.16.3 version: 0.16.3 @@ -90,8 +90,8 @@ importers: specifier: ^0.5.0 version: 0.5.0(@babel/core@8.0.6) dprint: - specifier: ^0.57.4 - version: 0.57.4 + specifier: ^0.59.0 + version: 0.59.0 fast-check: specifier: ^4.10.2 version: 4.10.2 @@ -111,8 +111,8 @@ importers: specifier: ^8.0.0 version: 8.0.0(supports-color@10.2.2)(typescript@7.0.2) oxlint: - specifier: ^1.85.0 - version: 1.85.0 + specifier: ^1.86.0 + version: 1.86.0 pkg-pr-new: specifier: 0.0.88 version: 0.0.88 @@ -120,17 +120,17 @@ importers: specifier: ^1.63.0 version: 1.63.0 rollup: - specifier: ^4.63.5 - version: 4.63.5 + specifier: ^4.64.0 + version: 4.64.0 rollup-plugin-bundle-stats: specifier: ^4.22.3 - version: 4.22.3(core-js@3.47.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 4.22.3(core-js@3.47.0)(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) rollup-plugin-esbuild: specifier: ^6.2.1 - version: 6.2.1(esbuild@0.28.2)(rollup@4.63.5)(supports-color@10.2.2) + version: 6.2.1(esbuild@0.28.2)(rollup@4.64.0)(supports-color@10.2.2) rollup-plugin-visualizer: specifier: ^7.1.1 - version: 7.1.1(rolldown@1.2.11)(rollup@4.63.5) + version: 7.1.1(rolldown@1.2.12)(rollup@4.64.0) terser: specifier: ^5.51.2 version: 5.51.2 @@ -141,14 +141,14 @@ importers: specifier: ^7.0.2 version: 7.0.2 vite: - specifier: ^8.3.1 - version: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + specifier: ^8.3.2 + version: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) vitest: - specifier: ^5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: ^5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) vitest-websocket-mock: specifier: ^0.8.0 - version: 0.8.0(vitest@5.0.2) + version: 0.8.0(vitest@5.0.3) zod: specifier: ^4.6.5 version: 4.6.5 @@ -225,11 +225,11 @@ importers: specifier: workspace:* version: link:../packages/effect hono: - specifier: ^4.13.9 - version: 4.13.9 + specifier: ^4.13.12 + version: 4.13.12 nodemailer: - specifier: ^10.0.10 - version: 10.0.10 + specifier: ^10.0.14 + version: 10.0.14 devDependencies: '@types/nodemailer': specifier: ^8.0.2 @@ -278,7 +278,7 @@ importers: version: 10.4.2 '@testing-library/jest-dom': specifier: ^7.0.1 - version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.2) + version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3) '@testing-library/react': specifier: ^16.3.3 version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -320,7 +320,7 @@ importers: version: 10.4.2 '@testing-library/jest-dom': specifier: ^7.0.1 - version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.2) + version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3) effect: specifier: workspace:^ version: link:../../effect @@ -343,8 +343,8 @@ importers: packages/effect: devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 ajv: specifier: ^8.20.0 version: 8.20.0 @@ -355,8 +355,8 @@ importers: specifier: ^0.16.3 version: 0.16.3 immer: - specifier: ^11.1.18 - version: 11.1.18 + specifier: ^11.1.21 + version: 11.1.21 protobufjs: specifier: ^8.8.0 version: 8.8.0 @@ -466,8 +466,8 @@ importers: specifier: jsr:^1.0.19 version: '@jsr/std__assert@1.0.19' '@testcontainers/redis': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) '@types/deno': specifier: ^2.7.0 version: 2.7.0 @@ -485,36 +485,36 @@ importers: version: 8.11.2 devDependencies: '@testcontainers/mysql': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) '@testcontainers/postgresql': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) '@testcontainers/redis': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../../effect redis: - specifier: ^6.2.1 - version: 6.2.1(@opentelemetry/api@1.9.1) + specifier: ^6.3.0 + version: 6.3.0(@opentelemetry/api@1.9.1) packages/platform/node-shared: dependencies: '@types/ws': - specifier: ^8.18.1 - version: 8.18.1 + specifier: ^8.18.2 + version: 8.18.2 ws: specifier: ^8.22.0 version: 8.22.0 devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../../effect @@ -541,15 +541,15 @@ importers: packages/sql/d1: dependencies: '@cloudflare/workers-types': - specifier: ^5.20260926.1 - version: 5.20260926.1 + specifier: ^5.20261003.1 + version: 5.20261003.1 devDependencies: effect: specifier: workspace:^ version: link:../../effect miniflare: specifier: 5.20260925.0-alpha - version: 5.20260925.0-alpha(@types/node@26.6.3) + version: 5.20260925.0-alpha(@types/node@26.6.4) packages/sql/libsql: dependencies: @@ -561,18 +561,18 @@ importers: specifier: workspace:^ version: link:../../effect testcontainers: - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) packages/sql/mssql: dependencies: tedious: - specifier: ^20.0.0 - version: 20.0.0(supports-color@10.2.2) + specifier: ^20.3.3 + version: 20.3.3(supports-color@10.2.2) devDependencies: '@testcontainers/mssqlserver': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) effect: specifier: workspace:^ version: link:../../effect @@ -580,12 +580,12 @@ importers: packages/sql/mysql2: dependencies: mysql2: - specifier: ^3.24.4 - version: 3.24.4(@types/node@26.6.3) + specifier: ^3.24.5 + version: 3.24.5(@types/node@26.6.4) devDependencies: '@testcontainers/mysql': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) effect: specifier: workspace:^ version: link:../../effect @@ -593,11 +593,11 @@ importers: packages/sql/pg: devDependencies: '@testcontainers/postgresql': - specifier: ^12.1.0 - version: 12.1.0(supports-color@10.2.2) + specifier: ^12.2.0 + version: 12.2.0(supports-color@10.2.2) '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../../effect @@ -633,8 +633,8 @@ importers: packages/sql/sqlite-do: devDependencies: '@cloudflare/workers-types': - specifier: ^5.20260926.1 - version: 5.20260926.1 + specifier: ^5.20261003.1 + version: 5.20261003.1 effect: specifier: workspace:^ version: link:../../effect @@ -643,7 +643,7 @@ importers: version: 0.28.2 miniflare: specifier: 5.20260925.0-alpha - version: 5.20260925.0-alpha(@types/node@26.6.3) + version: 5.20260925.0-alpha(@types/node@26.6.4) packages/sql/sqlite-node: devDependencies: @@ -651,8 +651,8 @@ importers: specifier: workspace:^ version: link:../../platform/node '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../../effect @@ -694,8 +694,8 @@ importers: version: 2.9.1 devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 packages/tools/ai-docgen: dependencies: @@ -713,8 +713,8 @@ importers: version: 2.9.1 devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 packages/tools/api-diff: dependencies: @@ -732,8 +732,8 @@ importers: specifier: workspace:^ version: link:../../vitest '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -741,8 +741,8 @@ importers: specifier: npm:typescript@6.0.3 version: typescript@6.0.3 vitest: - specifier: 5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: 5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/tools/bundle: dependencies: @@ -751,13 +751,13 @@ importers: version: link:../../platform/node '@rollup/plugin-node-resolve': specifier: ^16.0.3 - version: 16.0.3(rollup@4.63.5) + version: 16.0.3(rollup@4.64.0) '@rollup/plugin-replace': specifier: ^6.0.3 - version: 6.0.3(rollup@4.63.5) + version: 6.0.3(rollup@4.64.0) '@rollup/plugin-terser': specifier: ^1.0.0 - version: 1.0.0(rollup@4.63.5) + version: 1.0.0(rollup@4.64.0) effect: specifier: workspace:^ version: link:../../effect @@ -765,21 +765,21 @@ importers: specifier: ^13.0.6 version: 13.0.6 rollup: - specifier: ^4.63.5 - version: 4.63.5 + specifier: ^4.64.0 + version: 4.64.0 rollup-plugin-esbuild: specifier: ^6.2.1 - version: 6.2.1(esbuild@0.28.2)(rollup@4.63.5)(supports-color@10.2.2) + version: 6.2.1(esbuild@0.28.2)(rollup@4.64.0)(supports-color@10.2.2) rollup-plugin-visualizer: specifier: ^7.1.1 - version: 7.1.1(rolldown@1.2.11)(rollup@4.63.5) + version: 7.1.1(rolldown@1.2.12)(rollup@4.64.0) devDependencies: '@babel/core': specifier: ^8.0.6 version: 8.0.6 '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 babel-plugin-annotate-pure-calls: specifier: ^0.5.0 version: 0.5.0(@babel/core@8.0.6) @@ -799,8 +799,8 @@ importers: specifier: workspace:^ version: link:../../platform/node chalk: - specifier: ^6.0.0 - version: 6.0.0 + specifier: ^6.0.1 + version: 6.0.1 doctrine: specifier: ^3.0.0 version: 3.0.0 @@ -830,8 +830,8 @@ importers: specifier: ^0.0.9 version: 0.0.9 '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 tsx: specifier: ^4.23.15 version: 4.23.15 @@ -839,30 +839,30 @@ importers: specifier: ^6.0.3 version: 6.0.3 vitest: - specifier: ^5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: ^5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/tools/doctest: dependencies: rolldown: - specifier: ^1.2.11 - version: 1.2.11 + specifier: ^1.2.12 + version: 1.2.12 devDependencies: '@effect/vitest': specifier: workspace:^ version: link:../../vitest '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../../effect vite: - specifier: ^8.3.1 - version: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + specifier: ^8.3.2 + version: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) vitest: - specifier: ^5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: ^5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/tools/jsdocs: dependencies: @@ -877,14 +877,14 @@ importers: specifier: workspace:^ version: link:../../vitest '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 typescript: specifier: ^6.0.3 version: 6.0.3 vitest: - specifier: ^5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: ^5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/tools/openapi-generator: dependencies: @@ -908,24 +908,24 @@ importers: specifier: ^7.13.0 version: 7.13.0(typescript@7.0.2) rolldown: - specifier: ^1.2.11 - version: 1.2.11 + specifier: ^1.2.12 + version: 1.2.12 packages/tools/oxc: dependencies: '@oxlint/plugins': - specifier: ^1.85.0 - version: 1.85.0 + specifier: ^1.86.0 + version: 1.86.0 devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 typescript: specifier: ^6.0.3 version: 6.0.3 vitest: - specifier: ^5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: ^5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/tools/utils: dependencies: @@ -940,20 +940,20 @@ importers: version: 13.0.6 devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 packages/vitest: devDependencies: '@types/node': - specifier: ^26.6.3 - version: 26.6.3 + specifier: ^26.6.4 + version: 26.6.4 effect: specifier: workspace:^ version: link:../effect vitest: - specifier: 5.0.2 - version: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: 5.0.3 + version: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) scratchpad: dependencies: @@ -1642,8 +1642,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260926.1': - resolution: {integrity: sha512-jLtwSjBFtXucrEIj584vNk/k9OwgW4ij7/nK40hnCw137/0ilqfkTlYIUTWlTEbUX2V/+XVBijzZha9spdQTUw==} + '@cloudflare/workers-types@5.20261003.1': + resolution: {integrity: sha512-Uii0J5qUd/R6lIDK/u0Sw8dEi7oI3ljXw7EDybuPntoXg2Yy5Y9H9UeNQ5R87oQ/Q4jq6uEJmYOOFQ3Z7C94lA==} '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -1653,28 +1653,28 @@ packages: resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.4.1': - resolution: {integrity: sha512-EtC7SoN1j6J4E4DCwg5QgbO5TGxgxIA1RXqe+W+qUM+BUcezx9wT+/tiQ/WO2yCX4i5X+Cuf9ciJ22aP4UEwWw==} + '@csstools/css-calc@3.4.3': + resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==} engines: {node: '>=20.19.0'} peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.1 + '@csstools/css-parser-algorithms': ^4.0.2 '@csstools/css-tokenizer': ^4.0.2 - '@csstools/css-color-parser@4.2.4': - resolution: {integrity: sha512-DyefytAZ735mX4Dq/WcDAXFtXhaEFvme0ZS9tVEBAc2whxUthXr0R0L2rmEPm59SrMBkGrFzQviBXMs/UtnABQ==} + '@csstools/css-color-parser@4.2.6': + resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==} engines: {node: '>=20.19.0'} peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.1 + '@csstools/css-parser-algorithms': ^4.0.2 '@csstools/css-tokenizer': ^4.0.2 - '@csstools/css-parser-algorithms@4.0.1': - resolution: {integrity: sha512-ShL8BqPfbKJrJiKFH0xBbN0i7Nrh9HXYRuF+pzyj93R/BL2YAsUxJeqANErzqM+0JGl7vjHp+3OIgd/DL69YIA==} + '@csstools/css-parser-algorithms@4.0.2': + resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-tokenizer': ^4.0.2 - '@csstools/css-syntax-patches-for-csstree@1.1.14': - resolution: {integrity: sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==} + '@csstools/css-syntax-patches-for-csstree@1.1.15': + resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -1693,87 +1693,87 @@ packages: resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} engines: {node: '>=14.17.0'} - '@dprint/android-arm64@0.57.4': - resolution: {integrity: sha512-vpafWVSNbB5ShCThZC3O5xOPsdxc2oeLedFVCsscSRf/iR6WZ62ROU2AGcxsJyE2y4/vxivejenbNYjWxL24sQ==} + '@dprint/android-arm64@0.59.0': + resolution: {integrity: sha512-Ot0HzZPPGZVzj6/9/KElM+P470s9/zjAhVEEU7ThFmoA+JlK/JF3Uf4AxfSQcQxGezMV7KVAewCIIrFx1MhFpA==} cpu: [arm64] os: [android] - '@dprint/android-x64@0.57.4': - resolution: {integrity: sha512-Zv2I12Zs9IK8yv1wdXGWluUram9h8Acy3NuF/7WzsxIfdlmnWzUGxoc4Z0dno6aJpv0ViXybkIHPvDKrQnvxVA==} + '@dprint/android-x64@0.59.0': + resolution: {integrity: sha512-7Xa5INWMwGmPbXj2ifKluL1yxoqQIMNc8fq3NrCkGiIOI0890dyl90GkR0JcE1dqHVwIKFspvTw3gUG5coENsA==} cpu: [x64] os: [android] - '@dprint/darwin-arm64@0.57.4': - resolution: {integrity: sha512-2V1BzlP+5aLxMaq77MUn2EdRX+UtOq34QjyMwpq1wQkR5MAT2cglJro4Eh1tsI+btjt3FMsX4/PLvz6eUSLl3g==} + '@dprint/darwin-arm64@0.59.0': + resolution: {integrity: sha512-1woWHHVXXgpLcolRATsaowbn6OUpWj7dt8pPtop1Jg0n7tUoN2Y0Mwv+Wrh5Q8tOh+JTy9ytWDeA2cl+8GXibQ==} cpu: [arm64] os: [darwin] - '@dprint/darwin-x64@0.57.4': - resolution: {integrity: sha512-dQfzBLBQro1O+CxIkIVc9LJqgYsWCKXTf+3QA0Gq/NjW6KPBwlbiCquew3UOeWwGEvarNSA0/Rvz5J8HnykTPA==} + '@dprint/darwin-x64@0.59.0': + resolution: {integrity: sha512-NFoo3wuW+eB3+AGKqN2A9MF3K43yfxILjp+aKtAFAb3ZHajXaPj/0WqajUFH9Y41/d04oFenAmY/HQwsMNgh5Q==} cpu: [x64] os: [darwin] - '@dprint/linux-arm64-glibc@0.57.4': - resolution: {integrity: sha512-zFCgx2E2szEnMgtBCKiI/G5vV5mi7rnTVDMB2FrIa6JheqF8pgfrx6m3HWzIed74cFM3/KCH0LmIfCNZT5vRfw==} + '@dprint/linux-arm64-glibc@0.59.0': + resolution: {integrity: sha512-2/b4MdcoCyGBXhIbCytiGkS1RRWWLih3TM78a5Q1YiNJYl28zKx32Bl+zbLhGyaUdrYPiAuKZORHtcL2G5OIzg==} cpu: [arm64] os: [linux] libc: [glibc] - '@dprint/linux-arm64-musl@0.57.4': - resolution: {integrity: sha512-8DV33dU5c5vp/TJJp4Dy/4HYc4r5KidlMJgoiG0S9JMygVGjhrXRNiI/C7M9hLgsamqeLYt4MWfEGKvtgOwoew==} + '@dprint/linux-arm64-musl@0.59.0': + resolution: {integrity: sha512-3Oy/FLldjUSM/WnKQu6lvS508g6PPCyG9/lXD0y1j0zan80qTl2XCNR2oS5DfKPrtMbDoA/os5WFK/a7L90YDA==} cpu: [arm64] os: [linux] libc: [musl] - '@dprint/linux-loong64-glibc@0.57.4': - resolution: {integrity: sha512-166HbqGPMiKjCUuaiG6xyKrw8EKFF6vPBcmwFWUYHpZE6ipVVxt1Zen9957FXoxqyA5aspmK72eQGm72dQI2Yg==} + '@dprint/linux-loong64-glibc@0.59.0': + resolution: {integrity: sha512-hQ1TwhgY8TPRgAArDNB6i52OykU355GFgvuqxhx7AJGAQAm/FDUJRpTypt+KB4XGnUbqloiCSy2IEOUvTHqqlA==} cpu: [loong64] os: [linux] libc: [glibc] - '@dprint/linux-loong64-musl@0.57.4': - resolution: {integrity: sha512-TTNKhwDj4qCWSiP20pdktb/mF0MfjVnXEcCXAd37ll6lx+ri4M5aQKyeyFL1pVGU9msjQv++UTU7MGF9uKfOzA==} + '@dprint/linux-loong64-musl@0.59.0': + resolution: {integrity: sha512-y7Z9MdecQqyveRQzwSMpPKw0XrbOWyWxQtX1XJxtb9PalyCTzPHDkrO+lAjNm/35Rzp2DsZfUy/yVH126L/C/Q==} cpu: [loong64] os: [linux] libc: [musl] - '@dprint/linux-ppc64-glibc@0.57.4': - resolution: {integrity: sha512-kQGTaEoZIk+aHtEltz+nBQ54dhJ3dSxN4jxBOnK2wYCTaMkRnGxfF9Uwc29u/BL3Fw6ieDOJCSHy2VWFZdyctA==} + '@dprint/linux-ppc64-glibc@0.59.0': + resolution: {integrity: sha512-VJPuqiGkpP6OUYBF5Tzc/bx+L3LDXNmoWsujSwrQyxCiqctG+3OaWYAEbzFUNMylIRjCqAX0sO6FYXqiv5lI2g==} cpu: [ppc64] os: [linux] libc: [glibc] - '@dprint/linux-ppc64-musl@0.57.4': - resolution: {integrity: sha512-NuhwpDFzyBovfpI3M47eBUju8eCxR7tfrMpPBWXyl9YgX0JeBZt6clOV/fwJ2P3DE/ARD9xoyQSGgHzDoSFlOQ==} + '@dprint/linux-ppc64-musl@0.59.0': + resolution: {integrity: sha512-0bhSWqT5d00RTw2yMc9wV3ng2Hut6arJFjtz1gsj5sFUvmu+uksVTicBh/WYBvVxMLvMe91ZTUF8F5g4/cpB/Q==} cpu: [ppc64] os: [linux] libc: [musl] - '@dprint/linux-riscv64-glibc@0.57.4': - resolution: {integrity: sha512-pWBdHK8SKt9IGldbuZGbec37RyQqP4zqsWfpq7iyH+m33f7Aagus5ktvKF85KYfLTewIg+ev3vlxryB9iHBiNA==} + '@dprint/linux-riscv64-glibc@0.59.0': + resolution: {integrity: sha512-RfIDsOVtT9Nez+rbG9eIr21VOF3lmUpF/1qUUQOiFiisGWD3Kinm4pyB37HjS/OeN5Uv+hFalBACLdwC8cURZQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@dprint/linux-x64-glibc@0.57.4': - resolution: {integrity: sha512-BYrSdjYo+nfUgXqgIbZZQnHvpM6UP7Dipfsuzxs0JmuHZRRkJIPjDdj/Qpbn/5Qv1gC4eV0cZsxRu8B634wkig==} + '@dprint/linux-x64-glibc@0.59.0': + resolution: {integrity: sha512-jB+vpaB4LJ1t8HYagmcRq0/QDkFq5iTHcyIjBifB60yIHlm5ftt3hsSshEieqLgTQg1EbbgLkkLMgr9PCVoMAg==} cpu: [x64] os: [linux] libc: [glibc] - '@dprint/linux-x64-musl@0.57.4': - resolution: {integrity: sha512-pBRF5Kfv59FLtWYWWKpDLOv2+uTIr/XG2oMuRBiXXZom0b506aMm3zKCZ7z5ELeIYutPpASfkDbHcsyTY0ky6w==} + '@dprint/linux-x64-musl@0.59.0': + resolution: {integrity: sha512-k4uQbIqFKFOAT0h10c9FYFW/VgsnfbOp9hZXjWxq4OnWNbrFVugn25CcpnYrJ5zPVpvTiwjI0chEPE1NNhxpLA==} cpu: [x64] os: [linux] libc: [musl] - '@dprint/win32-arm64@0.57.4': - resolution: {integrity: sha512-bGEtcSjl6T4xjwr9nUF5kgd3174NP7yb338+Ulqq5DMYcSN4ibI62eVQKBTuyLWByz56aVOL06dj8pKTUzn+yQ==} + '@dprint/win32-arm64@0.59.0': + resolution: {integrity: sha512-OBBRuU5B0yvqagqCxsK4Q8+2ep8/dniIv9iw8fq7ikcNh8ljCxt29lHNDoGLsUwzSVPCw36ZiIWi7UxIfedI5g==} cpu: [arm64] os: [win32] - '@dprint/win32-x64@0.57.4': - resolution: {integrity: sha512-P91JJG2sDvK+SvZHdqjKygy7fYABbMH1KYop+WCowyjZUin09977qltdXwJ1vb77Cbi2OD0GWUuAlqO0PjNmaw==} + '@dprint/win32-x64@0.59.0': + resolution: {integrity: sha512-JBNGZ7FSCWIxX+cbBxeeqwSgsA4NTYNR8TBXFZ52+8fMutAtCYJ6oyCDk7TZVm9E09oOswasYgUpHTj+lwU4oQ==} cpu: [x64] os: [win32] @@ -1790,43 +1790,43 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - '@effect/tsgo-darwin-arm64@0.46.1': - resolution: {integrity: sha512-ErehNqI9p8me4XKO8N4bzdDWgBbSNlJ3CIZGfXoAW6Fo4Oo7x4ZbZV7KNsMSn9aJSiUUOQUoXjGBdslU5OYMdw==} + '@effect/tsgo-darwin-arm64@0.51.0': + resolution: {integrity: sha512-IsVT2Z7hNGT6RCLbNP/lyLyyjC9arVblfoXbr3qt3cF0A364BigXnoKx+NGm6ZNfg5NozW2NGKW2AD9btcy13Q==} cpu: [arm64] os: [darwin] - '@effect/tsgo-darwin-x64@0.46.1': - resolution: {integrity: sha512-zsQI1gjGMYF/C2FWDUwbStBAf0tJDVgXeWwTAZ+VUiNY06fR1SrZHK0L+22PE49Nim3biWK2h+XXRFVzql+ojg==} + '@effect/tsgo-darwin-x64@0.51.0': + resolution: {integrity: sha512-S0k97HUL7vS9a1CZFcZEUeOK/6fYuev06li7+canKpn9w1PAp+YtxrET3r37ZdHeZP7w/WYJtT0yxxLZO7Mprw==} cpu: [x64] os: [darwin] - '@effect/tsgo-linux-arm64@0.46.1': - resolution: {integrity: sha512-e283BZEQC3mJpk1N7vdUhoF/aYHc4YwCE/fwz/80d11OSvG35U+c49grxS7V7+dAl2ld6QiT8D1rcL8PYu8DLQ==} + '@effect/tsgo-linux-arm64@0.51.0': + resolution: {integrity: sha512-2KSJiPO3foagMgBuKhUBhO9X+JLHeJ/+2FzrwxQhexhR6V3LUnJ2L2hJS3KGs/sMn4TJjjtgczO8mAmIrFaffw==} cpu: [arm64] os: [linux] - '@effect/tsgo-linux-arm@0.46.1': - resolution: {integrity: sha512-Y12wOcJ6HgwiCgg/oDQH4tdRQKRsWnul/KjCOwMxzVT4kO+ndczu1sKUZT+qIxV7qNk4Sq+uSWKEnFBA3ETI8w==} + '@effect/tsgo-linux-arm@0.51.0': + resolution: {integrity: sha512-4FUwHI8G1Fbpa3nPBfR9143mK0ziLjfa+/RVzbr9VIfSF0FWEYHJzD7A3RGNEh0EXZ9ZQG+UKmfs9kKRhWOvyw==} cpu: [arm] os: [linux] - '@effect/tsgo-linux-x64@0.46.1': - resolution: {integrity: sha512-NYGBC3Scfo9yZJfs7cg0KMcHgtukJ4+xff18nqIrON9Zzc/qD6w1AwEwYKYCsbai5hTQBb3VLIWmHM8BJ54WJg==} + '@effect/tsgo-linux-x64@0.51.0': + resolution: {integrity: sha512-Psolf3poNm3PVHm/Q2Q66cXQ17ZIBLhtPJoKMvEIrlbAJmHMWfQyKeUrtRfum6V+yDTy6ZTeO6ksUfk0hg2YwA==} cpu: [x64] os: [linux] - '@effect/tsgo-win32-arm64@0.46.1': - resolution: {integrity: sha512-MK93SN9qjowrCjch+2wGGncd6Y7KgDyJvLKVVFCJV8cPpHoL3xDCpxFCGqQWe120mVpLmnzbM2evKdh0M5l5PA==} + '@effect/tsgo-win32-arm64@0.51.0': + resolution: {integrity: sha512-V+BiYnc75s30F62CQO2NtTFiaG0tYpBcVbIIvoiRBVuHUy68RDCXVQWqSNG6JkohTIabObPmvk4bznCLbcGvPQ==} cpu: [arm64] os: [win32] - '@effect/tsgo-win32-x64@0.46.1': - resolution: {integrity: sha512-hlRbeNlp0N/dfZZFl+xVzT3XfHYqPmkXzCadR6e5yoe/vyA3O9RnO5UrOBHsXvL2RTE6hrh8bRfx2Uu6g3LL5w==} + '@effect/tsgo-win32-x64@0.51.0': + resolution: {integrity: sha512-sO2Ngbdxn6DrDdSCItXu/J5ha7PgFGyY7U1qJ4VzMyovazguiVJfkExskZ174Qa+ltPWZIGHFY/QdVZwHepOTw==} cpu: [x64] os: [win32] - '@effect/tsgo@0.46.1': - resolution: {integrity: sha512-DMcBCg5nWLufejRlyqFEkXmbtaLUJ56NKny6L1LTtimvz0U3VNj9H6ZUXV4WeeWvJzVyL0y7G2wsCBAFUgy1jw==} + '@effect/tsgo@0.51.0': + resolution: {integrity: sha512-USDPT8AJCq1xc8+C8gyroohI0aJ9SZmmnSQRUEKzEMiBMJFpVuadXKqkHQ1DGOwZDeu2NDz/tLXxDD/vtel1bA==} hasBin: true '@effect/wa-sqlite@0.2.1': @@ -1994,8 +1994,8 @@ packages: cpu: [x64] os: [win32] - '@exodus/bytes@1.15.1': - resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + '@exodus/bytes@1.16.0': + resolution: {integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -2491,130 +2491,133 @@ packages: '@oxc-project/types@0.151.0': resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} - '@oxlint/binding-android-arm-eabi@1.85.0': - resolution: {integrity: sha512-q2KO/Zso9UT+OMn0NF9ywn4E4t0MI3yxiDhNyhsQ7DyQJrC4FhFE4TXOi4bktFnOWXTMds8qZSbpv2XwRaNOBg==} + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + + '@oxlint/binding-android-arm-eabi@1.86.0': + resolution: {integrity: sha512-63Ozq6yQn79B2UxjSZ9huJmI+3/ZPI7hxAe6TyFh9oQL447KnIMoffOdtog+h4KILbQr61ZRvFPW5gYbAUgpgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.85.0': - resolution: {integrity: sha512-SxLN3ALjoT9NNdvpjEevGeHvfzTAFrF0NBYB5tzK7/GtCKMze3j1e/m/X2ozqGj2U9hfGG/dg/OG8vpVK4PiDA==} + '@oxlint/binding-android-arm64@1.86.0': + resolution: {integrity: sha512-JdLGp44phbf/0Du236Dt06yfK6Qw8xhTFeDvHJdjk167r59OfP5+BL31mgMYVnswS8e+j6tFuccSY0QDC/lgCw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.85.0': - resolution: {integrity: sha512-Y/Sup/J4f0f9UGsSd/xyCNTeWL+gepO63GBdEDAfue9nBsnk9zMmnIXx1O6b1V8C90vB5nucYNZ0pbMXAp8zJA==} + '@oxlint/binding-darwin-arm64@1.86.0': + resolution: {integrity: sha512-h+vkOr4ik6KLFCXdtNVEj9xfnXfNUpPoF95QjORdhiSpLqQePzcN81kGHjAgsw1/G7WBWK5KsFKbI2UK8EyzMA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.85.0': - resolution: {integrity: sha512-ApOSNC04ynpDTwvBD+//0wyfODRSbEzvRoKpX8teffmc27z8AockwSNeMXGJXn5KP85eahDgR/2llICWLkzcnw==} + '@oxlint/binding-darwin-x64@1.86.0': + resolution: {integrity: sha512-wbglRuH5eKsp68keTOOeKDHU1astWSRYFsh2bwKQTAPb44rD6kVgzYI1ZrAJRTc6/7PH55ZXFCQx0rQXrj0UdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.85.0': - resolution: {integrity: sha512-bNrVrCOA/kHky3Tu79IXWXe5bhIgLXfUuUEDHlAGOHUk96MkvDZ1ecaQF19rwstrnaqfP1o9nBTqzIr9+ZHkUg==} + '@oxlint/binding-freebsd-x64@1.86.0': + resolution: {integrity: sha512-yTQ7QSOlNfQsrfbeVweTiegQJLKPjUKgXp/JpGiFzza6Hp29S9LAoH77TM3L0RjK39iQRZB5lk5iq2t241Mfag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.85.0': - resolution: {integrity: sha512-NUrzOJ1s/EqsVvfn2L/1D8Wro2LPIZUbihL8kOJLh5fEdGEN3rdOGUYq3HwnUIL8sjpoP+4N6RaGrgmMJnaMPw==} + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': + resolution: {integrity: sha512-QYJGy0E2Tv8GAayyBIeHFTwQb8u+zrvrOYrifuDGZDr9ad5XsDhiMScpZymhS9fwWjqrgG2ZUwT4vpY0ISqp4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.85.0': - resolution: {integrity: sha512-UJXrAT3E/RWkEqXLIs2ehETja1qfgkPb+5gwLIIS+o/6cf+grHvoOXTa5997a/YNQfcJS0DRBTOfZt95cvOI1g==} + '@oxlint/binding-linux-arm-musleabihf@1.86.0': + resolution: {integrity: sha512-oQo83CafLT8p3qh0AGkN0tSUIh5Hm63mfDsoqUmBQUBnSZLNBAbyRaAftfGY5a0cKZ34dG6jAtMYgcY5kZqt5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.85.0': - resolution: {integrity: sha512-lK40QLjI0HxigO7CjDDshEtfYIeiYS0020v5BHFPqN4uuQBQxd2K9LNom2dW15o9F1937quSCRVp4ZsVhdbYdg==} + '@oxlint/binding-linux-arm64-gnu@1.86.0': + resolution: {integrity: sha512-EM6wy5c2UM12qPb7iDJDBAZvHyfEJGH6iosFgaFiaRZpirz99//EHcUyD2KAskz07x1TULuiAtsojf4nvWWksw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.85.0': - resolution: {integrity: sha512-c2zbdBwGKreHXwRx3gWBuFGJxLhxgsg6YlZ+3H+RgRusU/UEV9jNwJ3HGYK+nRo0LvBa7mt6Kj86xoVotUo8cw==} + '@oxlint/binding-linux-arm64-musl@1.86.0': + resolution: {integrity: sha512-vCiUQb9ZNzZalxYRU4mlZwUqT5f+c7Sk9DwUOplWHtik3Dl4r3DVnUB5tBeRuTAUe6MUVCTylNW7TkkN8J1oTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.85.0': - resolution: {integrity: sha512-tlt/Hy8lZ97/lCPmCgw/B3k/mwh+BzaIPbPkldZEly7TwLmx0xe2CQcaW2g/rR0dOgS9JNGCZsMEqLhUNMGvaw==} + '@oxlint/binding-linux-ppc64-gnu@1.86.0': + resolution: {integrity: sha512-T0AzE89yjTYmOhB/hb6i+rWTpe2mYt+xiguBhs9DEPIJTZ1H8OYqsE4VxJHHw2Wlq8Mztwvp2P8NV87ZOuhPRw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.85.0': - resolution: {integrity: sha512-3tNR9Xey82X0zKuY1d8hJ6Rc9gwRDurmqGLnQZa5xqOXy8/YyiqFXjAtugkKLY82obOlpK1eSiDRlgcNPuxtIg==} + '@oxlint/binding-linux-riscv64-gnu@1.86.0': + resolution: {integrity: sha512-s49r0K2f5Oc7fJoMUO1WkdhYK5Ob79uYRwWQ/XNoGysaOq3RUGmGUbHSuXiyydTTA+P9xEdDEnnlZVXiaSJhlw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.85.0': - resolution: {integrity: sha512-wbGRd5PqCcjkJFHhZuZ2OBSUQY9czlQsoA/cQQB9JK/L9mC5MQgGoKAh+xd8QjA5V+0D3j+Qd1lAWn1I8zlelA==} + '@oxlint/binding-linux-riscv64-musl@1.86.0': + resolution: {integrity: sha512-JIa2tYR8ayb3nvcMSZzn4O/xTy13S7MHllu7cBD+B5qDQXXSRy/Vwk1aNpiTjZmBJGwKCHOy/NvnY5Xcwg27uQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.85.0': - resolution: {integrity: sha512-3Sn0kSrE4DPZCWV/8o+n4x3aFZxI9ulMnkYlwCbJ8eUVkwRK2IerohE/A/z3SNbCwoPFOCJmGE5Avrq0rrvdvQ==} + '@oxlint/binding-linux-s390x-gnu@1.86.0': + resolution: {integrity: sha512-XwXMyBsAuWZkhZU74UNcfQKr1CJJAKnS80dZzOnjl6A4fKcf91IjFgidegQugMHA5SN8ulwDXEDV1yPntjXgQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.85.0': - resolution: {integrity: sha512-JY2pxxYfB62bAGfejljVCqc44etItehPuAyaeSAdMuEMtwNA00ggMnS66lC1oIhos6oOXUkuU6mZ9bpFh3BqWg==} + '@oxlint/binding-linux-x64-gnu@1.86.0': + resolution: {integrity: sha512-C1WjukSyMnr66b+w1/tV8RFVv6d9v0MzDf4p9IxVXknqgmTHBgZh1pccN1eHzFr0b9Tbb3OXoPsAAdAuHAQfeA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.85.0': - resolution: {integrity: sha512-5k74vZ6qJBjBHEOlBk9B/iv68Yu0F1Afw/vvT2ar6OGCqEeXLaSjXz2n/IPCbhLG22UoKoYEJTzpYraRdcp6PA==} + '@oxlint/binding-linux-x64-musl@1.86.0': + resolution: {integrity: sha512-ap6KLmvC38c6MdYzsIh25cXQupqYvjd37tMNftzrX1DCtkX1Gcf2B+S2B17dD4lKa5c5gJog7DQJyDo82PBKyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.85.0': - resolution: {integrity: sha512-GbAl5qt5TCkPLXTaIISZJnugrcBhra6rodcXc9jYt620UtdsTt71NlNmJmm0frxzFpd54x/G+MkitEJA8I/BoA==} + '@oxlint/binding-openharmony-arm64@1.86.0': + resolution: {integrity: sha512-996Q9w/2GLwNyy5Djs39Jt09aFlr4b7TPhoj7dvo5FHmcyiF68nPz73HNc5UAYKIYeK+M9JzPQ4Wg1r1dXvP6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.85.0': - resolution: {integrity: sha512-kjmws5MK0et2swk4ND85D7NVQyDHw162i6whtZDLUA/lo6FQyBZDcmMRCMcVZcNrAhIaftVb00x9ChGDOjjNJA==} + '@oxlint/binding-win32-arm64-msvc@1.86.0': + resolution: {integrity: sha512-9uTCHYTknNkug5+46ViSoUMuOuGWK61D87QnblU6CXaWKJC630Z7dbls61vdHxya/cR2ycQZ6Mm6ckgF6Cag5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.85.0': - resolution: {integrity: sha512-eSsIJx9n4yxvOqYTZyPEMyEXRmE60XH7xGAU7i0Qbsn1lf6Za3CWJ9aRd82oSFKXaxhp+sA6/yMJVRIpLpna6A==} + '@oxlint/binding-win32-ia32-msvc@1.86.0': + resolution: {integrity: sha512-a47ga7EKVfMU8rO0i1m3mGwfNRj3FhQyt1d/ukO/E9u1HHF3KpR3tyb3VhuJBGEVPF8uR4exSyHUyx7xbE+uxw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.85.0': - resolution: {integrity: sha512-pBebIPUpKKhWrhSMWhy8TdAZBewiXnfxmaAGxhzxM1068GagqFaTwgKlU6e+UyJ2sPR+VoHouhXuGJkQjsrDvA==} + '@oxlint/binding-win32-x64-msvc@1.86.0': + resolution: {integrity: sha512-az4dzifCRzES1crwu6pDFXzJTIanrdxXFL9TbiJWrx9LKcuacbR83aPvxDA1LKLNswi/RoqitC7Afcflhu3zBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.85.0': - resolution: {integrity: sha512-qqHqML3ME3cxGgFvWJG8wwMnJc9ol5SMdtqkq0k8r7l0USmC+HRXYjzDacME4Dqkx4yqrUsBhj0JYvji8fHxJQ==} + '@oxlint/plugins@1.86.0': + resolution: {integrity: sha512-JeAiaaRMyUiUEQHQ7zBsJn1aEdbLyt1HJsOs4XKIIlATNxcvWRrRxneZA+RF4G03cQobq3IhTqSfyLhOFv03qA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pkgjs/parseargs@0.11.0': @@ -2717,14 +2720,14 @@ packages: '@types/react': optional: true - '@redis/bloom@6.2.1': - resolution: {integrity: sha512-huQgNLaCIZfQ9SeLn4q9124uOUd8HbZDYHwwUzNcRgHqCHiHKl2dDxMqJCeWh8cMqZAoWuHR8XnWbDMIf+o7ag==} + '@redis/bloom@6.3.0': + resolution: {integrity: sha512-NQ5poYpZr0jv6zazN1cy6JY5kIipKFZ3R9GTL4kOUTX3a9YAfY29U8bsE4lWhVQEM+G4V0BXVC57mubIdDijeA==} engines: {node: '>= 20.0.0'} peerDependencies: - '@redis/client': ^6.2.1 + '@redis/client': ^6.3.0 - '@redis/client@6.2.1': - resolution: {integrity: sha512-LzxBY7SIBvvJiyCgcaJZZakE3fJrZZ++i24+EDW9fKpCl68D35uJcKFpZZwCfOoG9WZTbyZlMzMeM0gtOAMU9Q==} + '@redis/client@6.3.0': + resolution: {integrity: sha512-fHrjwGCBvAANGi+TuRuwKi83sIOzgHr8eh+Uf48w3fSX7Hhrz174NrWqfJhAC2vnN0d/tfaZpdr6+o6JC3QHwg==} engines: {node: '>= 20.0.0'} peerDependencies: '@node-rs/xxhash': ^1.1.0 @@ -2735,23 +2738,23 @@ packages: '@opentelemetry/api': optional: true - '@redis/json@6.2.1': - resolution: {integrity: sha512-AFIUJ8Gj0DaaSBHYuSt8+O0oYWM+50OK1c0OmodB7XERIA8+BbyV3O4v76f9iccWasd1/7qjfZTpuzexUaZtrQ==} + '@redis/json@6.3.0': + resolution: {integrity: sha512-yt6vRfPBXtW/3qm53cF81TSQpLMjWdW4/2E9WQ2GgbvsKkuCKCFQIcMiKlil1ZMnezK+IQuS795xKU5psbaegw==} engines: {node: '>= 20.0.0'} peerDependencies: - '@redis/client': ^6.2.1 + '@redis/client': ^6.3.0 - '@redis/search@6.2.1': - resolution: {integrity: sha512-2vfOAOyYFE7UUw3sBBlkqqruBtOUS4HRY5MtW4hp83llrwvtrTE4r22CEqXddlV+54zkLxBE4nmsIJ/dpezQrQ==} + '@redis/search@6.3.0': + resolution: {integrity: sha512-RPXAZmKjZZG7Tm7m1w9keFdXzbL91PiSHQBfQRe9xwW40jNsTRbG0FwhIz4QlHPU4gKDAsCy8IQHj4yZl4H9rw==} engines: {node: '>= 20.0.0'} peerDependencies: - '@redis/client': ^6.2.1 + '@redis/client': ^6.3.0 - '@redis/time-series@6.2.1': - resolution: {integrity: sha512-kiYniph04dJOole+L359B6C9E+jYS2uDP7hca6Onj0xF38ZIpyxARO0Iq0W4ZRn1e8Q6vqW00QFZVSMRA/2Ijw==} + '@redis/time-series@6.3.0': + resolution: {integrity: sha512-+fJOB8mN1z5WRakCJNh5lVdz1KqvHGSOistsKeitD24QsCtPwyRQXWC0iq6T/i/ubLE/YcguZqPm8F598MKk4A==} engines: {node: '>= 20.0.0'} peerDependencies: - '@redis/client': ^6.2.1 + '@redis/client': ^6.3.0 '@redocly/ajv@8.11.2': resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} @@ -2769,36 +2772,72 @@ packages: cpu: [arm] os: [android] + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + '@rolldown/binding-android-arm64@1.2.11': resolution: {integrity: sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.2.11': resolution: {integrity: sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.2.11': resolution: {integrity: sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.2.11': resolution: {integrity: sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': resolution: {integrity: sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.2.11': resolution: {integrity: sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2806,6 +2845,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.2.11': resolution: {integrity: sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2813,6 +2859,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.2.11': resolution: {integrity: sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2820,6 +2873,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.11': resolution: {integrity: sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2827,6 +2887,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.11': resolution: {integrity: sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2834,6 +2901,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.2.11': resolution: {integrity: sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2841,24 +2915,49 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.2.11': resolution: {integrity: sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-win32-arm64-msvc@1.2.11': resolution: {integrity: sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.11': resolution: {integrity: sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -2898,141 +2997,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.63.5': - resolution: {integrity: sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==} + '@rollup/rollup-android-arm-eabi@4.64.0': + resolution: {integrity: sha512-FLLpX941CD/7aBvX3GqLGBM9OvY5PGXTryPJkG5r5/OuBpZpV5k45Uo3RxAvqd35tsyc3hH1bhtz921IKzoIzg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.63.5': - resolution: {integrity: sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==} + '@rollup/rollup-android-arm64@4.64.0': + resolution: {integrity: sha512-iI32XM/jnDYJsvx5ai/za/7gWO9XoSeFFtU06/ZCaRgyilfJOjaopD3nOpk2WrTCGOyeDt4hleUiGOAnd5tHNg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.63.5': - resolution: {integrity: sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==} + '@rollup/rollup-darwin-arm64@4.64.0': + resolution: {integrity: sha512-dRBZlbt2sEtF8Mn4MODThze9fIPdDoOhVSlXMAKy5dQbOYK9pOOoPY4Fat3OTtr0+8zbJo5TTZwlsfxY/kDNJg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.63.5': - resolution: {integrity: sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==} + '@rollup/rollup-darwin-x64@4.64.0': + resolution: {integrity: sha512-Y3whrJOMucfkhgNkvcM93M6j8C53iEAMmPMYD99C6G1kUy4ELigLWk9ar2/4aCBSRgsql0zdlOemLgW2m5GZgQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.63.5': - resolution: {integrity: sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==} + '@rollup/rollup-freebsd-arm64@4.64.0': + resolution: {integrity: sha512-S6S1GEzPjFF6IONYJX3AKt/799+8RgBgCcwRnUo+Ep1IvBxUbw5M51iU0TDdK2NWWVnSvCQ8yi6Em0fGASMOng==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.63.5': - resolution: {integrity: sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==} + '@rollup/rollup-freebsd-x64@4.64.0': + resolution: {integrity: sha512-Ed1DPoFENkXj4IGzSB12SesV40iP6LxCCrbTIkSGIQBpOOxpBM+0kj62lmaeIHfMWTDoddqTOtqc7s8LYfn+Pw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.63.5': - resolution: {integrity: sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.64.0': + resolution: {integrity: sha512-J/6xhutJPtFyIkmHdf4olOdHHKH5kOJNumojdWn+Elv5ICEYTVOa362Vw27TxsK0BLzb1le3EDlO3UbvFK1lnQ==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.63.5': - resolution: {integrity: sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==} + '@rollup/rollup-linux-arm-musleabihf@4.64.0': + resolution: {integrity: sha512-G4L/Nnx59d2LvDoV/P4S5lZ7oVLT6hodOHMacqjiq2LL2gts86KbkEBTYL53+fBt0odhf7XPIbIATGoYiJctxg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.63.5': - resolution: {integrity: sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==} + '@rollup/rollup-linux-arm64-gnu@4.64.0': + resolution: {integrity: sha512-zF16pvJDEUksGYRbfnXne7pdklAyj2XDmJeYKgP93oq0cUyX9Y8O7lUo1R+IY1GagP/YBvCEDCe7eEcAKpw//g==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.63.5': - resolution: {integrity: sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==} + '@rollup/rollup-linux-arm64-musl@4.64.0': + resolution: {integrity: sha512-nIggWn8n+w12+R5lsV+PYYoUY8vuObxPAQqJI7WES+9v0qspYuAHoZMfYM0yLxlipasT5BY9ht5W8t5c/rSCkg==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.63.5': - resolution: {integrity: sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==} + '@rollup/rollup-linux-loong64-gnu@4.64.0': + resolution: {integrity: sha512-WLOTiHPzszP0dxIMsHpFeDSOSB5I49Mj8K4+yKE3H1X/D0JRG5XZ5jrctJLMjnUc/LUzaKiwq6EHWNJ8pup8PA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.63.5': - resolution: {integrity: sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==} + '@rollup/rollup-linux-loong64-musl@4.64.0': + resolution: {integrity: sha512-35RK9aRhh7g7Pmeu5gQ01aCvrhXzj9LoBE6xtUcEH1ns+nyTaSzxSpADmBQINu4QiTDGvmQYwo1j5hlg1rRXFw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.63.5': - resolution: {integrity: sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==} + '@rollup/rollup-linux-ppc64-gnu@4.64.0': + resolution: {integrity: sha512-RqTfnpoTjuWfPAmqK5p2edt2AMCDJWm6oTn7sKfmyx+6tRga0ip05SJ977o6udTcIiDeF/CJMUfkaN4gtWt6Aw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.63.5': - resolution: {integrity: sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==} + '@rollup/rollup-linux-ppc64-musl@4.64.0': + resolution: {integrity: sha512-RQjZPigjxFvFZDijG6rjlSXxhRaTf9zLYVpGK+lVcga64oMqP1GbgVqCb46eHTTceSIgp89Ja0y5aPe3GFBYHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.63.5': - resolution: {integrity: sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==} + '@rollup/rollup-linux-riscv64-gnu@4.64.0': + resolution: {integrity: sha512-tjL58mgQcTRnDRpeky+Sth1MKQeYi2Ct6r3Luq9E5aTSjnM/i/JbozqZkKqp9thM5+oDSZxo+05f9kdQedfTGA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.63.5': - resolution: {integrity: sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==} + '@rollup/rollup-linux-riscv64-musl@4.64.0': + resolution: {integrity: sha512-ictoRyVaoZ8C67myHef+8hhCa6IplXJJ/xUCkVCxziV4zxMZh+jvhkhGsPBLqj1S72fnqD0QH5zL4lcaa4X7DQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.63.5': - resolution: {integrity: sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==} + '@rollup/rollup-linux-s390x-gnu@4.64.0': + resolution: {integrity: sha512-8aVvZ2hfr9RLFzBQkQNmYeHLDja8fLeQAmLt757zdKN/B+8L809KlLJKctQjS22wZotptV8G6o7fVPuAs8S1tQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.63.5': - resolution: {integrity: sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==} + '@rollup/rollup-linux-x64-gnu@4.64.0': + resolution: {integrity: sha512-2dEF8GAcDKwshUfydD+GhosppNyBzhVUHkdFF3CXd3FpOkzj/RZzF5aoEtzklujklT5qFVRU2GK/cFeLaTNKVg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.63.5': - resolution: {integrity: sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==} + '@rollup/rollup-linux-x64-musl@4.64.0': + resolution: {integrity: sha512-3XHTvoo2hKkh0MiOfS7SkhHjMaaWF7P4F3drocZZaho/Z8w7h/jpnuBHm91BOQLfvrKHs1yR+zir4E8RI3XzTQ==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.63.5': - resolution: {integrity: sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==} + '@rollup/rollup-openbsd-x64@4.64.0': + resolution: {integrity: sha512-HKtkzodL4a3KFWDXDoO61cRynAWTR3LnAE0oGtfG9lcNFpNuVYUFOByxDgNqEvic9rvaQbAj+SFwTEWDptRaIw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.63.5': - resolution: {integrity: sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==} + '@rollup/rollup-openharmony-arm64@4.64.0': + resolution: {integrity: sha512-JBsxqiY16uyyCujtoC3s7zL+SrYyNElptD7K4V+KUoJ2V8/XG3hPWpheK+SuPwNh1cikJvIZmIdsrYXvD6PLJw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.63.5': - resolution: {integrity: sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==} + '@rollup/rollup-win32-arm64-msvc@4.64.0': + resolution: {integrity: sha512-S5Qrijh37qhnUYQ0ay8WN3LuWeKjow7wTUyCOPMZtnXiBXYXdsZETI6ZLfYnQAKovltckPLBFdNeD61P2rJc7g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.63.5': - resolution: {integrity: sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==} + '@rollup/rollup-win32-ia32-msvc@4.64.0': + resolution: {integrity: sha512-zbZ6iMmLFEsCtTdc2/y1F/cDFVNNCPF7Ae8PCT/wdPE//hGwYp6r+MyCLMFX+kOeIe09Pz6ASfnnfpSTN1FJQQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.63.5': - resolution: {integrity: sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==} + '@rollup/rollup-win32-x64-gnu@4.64.0': + resolution: {integrity: sha512-L6Hw69oNpuYaOr1zCG30v0/DqKYCSRwodPGwIcVZ/6f8SOzq9rxtL/xNVI5MO4wjli+Ko0ErK9uHJL3mNPt8wQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.63.5': - resolution: {integrity: sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==} + '@rollup/rollup-win32-x64-msvc@4.64.0': + resolution: {integrity: sha512-nl+CTWIgOGszQmUz/kanfgrfFZ4qiwxKIfT/ryl3N3Ewk5tqizmYtNXiqb7Bu/fDtNgu1kyxdFiAUJNur3iysg==} cpu: [x64] os: [win32] @@ -3104,17 +3203,17 @@ packages: cpu: [x64, ia32] os: [win32] - '@testcontainers/mssqlserver@12.1.0': - resolution: {integrity: sha512-DesPv/vgd6MBGzML5qWZyhIxREDdcdlzJ39WsH+lDgx6287mLZ38/v02GGeeOsv6P0Tw2TElp8lBieK5wH4Xhw==} + '@testcontainers/mssqlserver@12.2.0': + resolution: {integrity: sha512-TKqljdFoPiAPztyoHY5jRpkoMK7YrMz3g0/icJQ1bhMzwi7vo3TyDKjAbiPt0sPEdNmJSXapWw4o7On86arbgw==} - '@testcontainers/mysql@12.1.0': - resolution: {integrity: sha512-fNuN+Fs2N0EQBSk6HVplSl7qxFGUX9gzNw0ljr903PbBhvy2BM3Wl4kVAa1lFpizb8cY8mKYAs0C8cDL5JRIAg==} + '@testcontainers/mysql@12.2.0': + resolution: {integrity: sha512-Pni1nHgh01s/w0od+BL4ostBByfN7M9i20bm4iFT5xr4eAb6rBw2YT6wz6kfYNsx95Tj2TkCiRwkSG085VlNLQ==} - '@testcontainers/postgresql@12.1.0': - resolution: {integrity: sha512-Pjf2VSVNirEPfz36nidyrVAnZvc2YhajOznY4VgyEsvfTd5qiMNOuPq96drREvxAUtXl5SFLX7vXj7sSq4aTcA==} + '@testcontainers/postgresql@12.2.0': + resolution: {integrity: sha512-14oNBbKdqIkWTiw8XwP6YqWnuBebiij8m4fF03WTSHvNt5FfvQpGTLwIV3p07UpDnqWRFvY50YFzbiYdB+SWuA==} - '@testcontainers/redis@12.1.0': - resolution: {integrity: sha512-WRiS1Lhc+e/DHXDnCmMZtm8Wv64zYlRGNV04rvtn8kxIS2aVz5lgsP+Su/cfBzHJ0/vwdVKW+YJ3nUrXYbQxqw==} + '@testcontainers/redis@12.2.0': + resolution: {integrity: sha512-ZwD+aUL5LDHT4BIVzzbpPJitXqUibKt8UyFgWGzQbw9YoIaQdRrBbdY7Y+L4th7nYwmV+1UI1iO9XtJzadXnAw==} '@testing-library/dom@10.4.2': resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} @@ -3230,8 +3329,8 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@26.6.3': - resolution: {integrity: sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==} + '@types/node@26.6.4': + resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} '@types/nodemailer@8.0.2': resolution: {integrity: sha512-c7M5ox8p0nEOfbJ2E9Qcmt8/QCu/VzsiAhzBiZbvky2PhKZDHpKB3sQHBM5MEZNkCfBOaS6S9//AGPRTNB7IaA==} @@ -3274,6 +3373,9 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -3430,25 +3532,25 @@ packages: resolution: {integrity: sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==} engines: {node: '>=22.0.0'} - '@vitest/coverage-v8@5.0.2': - resolution: {integrity: sha512-3ffHBEi8DOOBLwIGBhOBZbRfYFYWjMUuxZicONdhuFEFEG5AVsMOSrVeuROskqnqpbOmEJwxM2eTVZ9N3a1t+A==} + '@vitest/coverage-v8@5.0.3': + resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==} peerDependencies: - '@vitest/browser': 5.0.2 - vitest: 5.0.2 + '@vitest/browser': 5.0.3 + vitest: 5.0.3 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/istanbul-lib-coverage@1.0.1': - resolution: {integrity: sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==} + '@vitest/istanbul-lib-coverage@1.0.2': + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} engines: {node: '>=22'} - '@vitest/istanbul-lib-report@1.0.1': - resolution: {integrity: sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==} + '@vitest/istanbul-lib-report@1.0.2': + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} engines: {node: '>=22'} - '@vitest/mocker@5.0.2': - resolution: {integrity: sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==} + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3458,13 +3560,13 @@ packages: vite: optional: true - '@vitest/spy@5.0.2': - resolution: {integrity: sha512-Ijc7T1nT9efNb5LxvjaBrEqw3f/QwUv5EE0nKqZxgqsaV/FxAAZ8baGylA8X/Z2oS4Lp+K74Jr6dTJsDKxJDeg==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} - '@vitest/web-worker@5.0.2': - resolution: {integrity: sha512-5/sysAtw/0Xt75i1aZ4tfj40C6oTjMecXv4vCmlVy0j3oZvm7cmy0BkpIdWdUiid6FIihF69RaWXVRZfVrCYOw==} + '@vitest/web-worker@5.0.3': + resolution: {integrity: sha512-wmxDW6gehdUkQN0MeTIZ3UN/MtSKj+FcJJJhtCtWwGVSpk4tSdlo+qe3EArYurqb5o7aC2hurGSCbhHzsHKKxw==} peerDependencies: - vitest: 5.0.2 + vitest: 5.0.3 '@vue/compiler-core@3.5.43': resolution: {integrity: sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==} @@ -3794,16 +3896,16 @@ packages: caniuse-lite@1.0.30001812: resolution: {integrity: sha512-qN+QNNBr93TCmFrmte0bBCjSDMuRvt78VlHT99qIGPszm4QsqCX8lnyWUFkHi8B7ZBAPq8SH+UqyXNy/odMdng==} - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@6.0.0: - resolution: {integrity: sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==} + chalk@6.0.1: + resolution: {integrity: sha512-/Ce6KNm3vIbWdMlNna6RVIZ/ICQxnJxCicet5LBKK9ZffBkqzDw0xh9EiKSljdRtiIQ1S1z4YgcscUUGzNCWrA==} engines: {node: '>=22'} change-case@5.4.4: @@ -3863,10 +3965,6 @@ packages: resolution: {integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==} engines: {node: '>=0.10.0'} - cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - code-block-writer@13.0.3: resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} @@ -4100,8 +4198,8 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dprint@0.57.4: - resolution: {integrity: sha512-iiorgfJrrV7DDf7S9AB/d5ZB59Jn5idMDQoU7o6H9m6AvGVaLAPrGTAslmTwykkft9CYbWzqUrKjmKT80KWmAA==} + dprint@0.59.0: + resolution: {integrity: sha512-oVCCCZPS1uFFOGIuw+ioaNIkwpwV3Cb+J0VKdFpxwtHpwzZewsBPgVvBuAjeeN55yCnRlEoMMSpBLB8GwvyGtQ==} hasBin: true eastasianwidth@0.2.0: @@ -4461,8 +4559,8 @@ packages: hermes-parser@0.35.0: resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} - hono@4.13.9: - resolution: {integrity: sha512-7dMkQmZoC4E6F7AtaQSPhlWAdnBti+j7rreMZl8QB4jFiEhP9TWbGWUMi8WYzBCgmgulxuvLQupKqo+Co6Omyg==} + hono@4.13.12: + resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} engines: {node: '>=16.9.0'} html-encoding-sniffer@6.0.0: @@ -4500,8 +4598,8 @@ packages: engines: {node: '>=16.x'} hasBin: true - immer@11.1.18: - resolution: {integrity: sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==} + immer@11.1.21: + resolution: {integrity: sha512-bZSadeCPGdaY5/qRLgr6mDSH1ra21rD3rqpiWk7+QTr4ayYPTE9qZYdLh/NdPhMe0wSrTvHD/Lk8q5EwhUs0aw==} import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} @@ -5166,8 +5264,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mysql2@3.24.4: - resolution: {integrity: sha512-A2olluVlj0mvgyIRRISMEzXc51m+21mRtcMVjJyIpt2GG98+XrC9m9HzsqcMsX2LcnfccJvY5NB22g8fENBnOA==} + mysql2@3.24.5: + resolution: {integrity: sha512-X6Ujsr2QSkkLpkQGjxzpKRAPn9nu4axpR63ntBzquFVEvPOArgbUQ1sJjFKI7hnaYtiVZxa17Z7q18KSubW0IQ==} engines: {node: '>= 8.0'} peerDependencies: '@types/node': '>= 8' @@ -5229,8 +5327,8 @@ packages: resolution: {integrity: sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A==} engines: {node: '>=18'} - nodemailer@10.0.10: - resolution: {integrity: sha512-He9XskOFms62SyAKkLu8CcGcYHAo+BSHSsORI8s4PJH8qZgZY4L4lDfvyN0twvmbpyG+eGrxpyBlskj9d9rJ8A==} + nodemailer@10.0.14: + resolution: {integrity: sha512-eJoLFKg55fesSlzUekmfOv/SBXh4KUYT8oiQc2RLP0tfG0rbNMJBK7wgHJwXTJwqyKlSJMz9UzDZ4v+aHEvo8g==} engines: {node: '>=20.0.0'} normalize-path@3.0.0: @@ -5308,12 +5406,12 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - oxlint@1.85.0: - resolution: {integrity: sha512-bc26s97nuvPj1ViyPsqmKecVkUWFMEdtayO8MaQ6oiLfs1pj94cQlZZhrh4BPNlr9HQosjhIlwgZKsfcwmcNgg==} + oxlint@1.86.0: + resolution: {integrity: sha512-og0lhgvZfgGF//gOOmZXvtr+GmBbAGEnbEhv/QUg7UW2Wi4wHMnJbnMD+zuHgPdJxdklfgpPupdlAaAySyxrZg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=7.0.2001' + oxlint-tsgolint: '>=7.0.2003' vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: @@ -5604,8 +5702,8 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - redis@6.2.1: - resolution: {integrity: sha512-Z9VHtgYs48PiQC77X9O2Er8Hj4T+5BtFjT91/vi5Is1D04N72cA946ZslM1ImJw8ZctFBZWAVjM7S5wJNeHMpg==} + redis@6.3.0: + resolution: {integrity: sha512-XFQbPie1lGpKeUZ8ySYY43yLnQy/iIQtHNvIr/1XdaABpb96rUSdm3Mfl+98FEVbv9ZJIzINMno5s0Nh8E5LEw==} engines: {node: '>= 20.0.0'} reftools@1.1.9: @@ -5678,6 +5776,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup-plugin-bundle-stats@4.22.3: resolution: {integrity: sha512-/VfAuIL+rjL6GLETHoQJ3RgU2g6QxL4704W6+CMwKs6VM4k1/i04py06U9nsEw3aAKTIlrNMnijC2rDKciFOgQ==} engines: {node: '>= 16.0'} @@ -5743,8 +5846,8 @@ packages: vite: optional: true - rollup@4.63.5: - resolution: {integrity: sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==} + rollup@4.64.0: + resolution: {integrity: sha512-gjA/kFeDfffII8COoPsvLPvE16hWDTEc1+0QKQNKUkuV8iJjqsC4xXs8/FzuL0v9EV39kuxTbAgcFoTRyy2TGQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5897,6 +6000,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -5951,8 +6058,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} stream-chain@2.2.5: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} @@ -6076,8 +6183,8 @@ packages: resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} engines: {node: '>=18'} - tedious@20.0.0: - resolution: {integrity: sha512-bTR0aou0Ghucf0ytvZUJjnKHGKDV8tT57jPYtEkSpfTWFe++4uR1wxJLQ4mh5wlSvAYXzmgBxbI0vaE56qigXw==} + tedious@20.3.3: + resolution: {integrity: sha512-PgK/FhX/I4A6IfuQrZ03pok0hXa2LSVsvfXR+EmLeagiFhXzm97UtKQI4+lDDdQx9sKg1U2ag0dN8k7CQRgPhw==} engines: {node: '>=22'} teex@1.0.1: @@ -6092,8 +6199,8 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - testcontainers@12.1.0: - resolution: {integrity: sha512-YjDLqIITuhGLMnM10yhg3oV6lIG5IMpz1R1DPBZoOOks83q7i7IVpeSWRTiyl7roozjiyLmwIoLK/KY8OnZmIA==} + testcontainers@12.2.0: + resolution: {integrity: sha512-Pd0c2eU0DdIuK+Pb3c+vmBUdKjDLgxZmPecQGESFAKyn5LTqSaSw8l7htfMlLwabb/QsUILe6XkxMJw8Ly/ThA==} engines: {node: '>= 22.22'} text-decoder@1.2.7: @@ -6120,8 +6227,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} engines: {node: '>=14.0.0'} tldts-core@7.4.9: @@ -6309,8 +6416,8 @@ packages: resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} engines: {node: '>=18.12.0'} - vite@8.3.1: - resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} + vite@8.3.2: + resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -6357,20 +6464,20 @@ packages: peerDependencies: vitest: '>=5 <6' - vitest@5.0.2: - resolution: {integrity: sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==} + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 5.0.2 - '@vitest/browser-preview': 5.0.2 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 - '@vitest/coverage-istanbul': 5.0.2 - '@vitest/coverage-v8': 5.0.2 - '@vitest/ui': 5.0.2 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 happy-dom: '*' jsdom: ~30.0.1 vite: ^6.4.0 || ^7.0.0 || ^8.0.0 @@ -6445,8 +6552,8 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@17.1.1: - resolution: {integrity: sha512-ohjk1mdUebJVadRt3bAhQhx8lSnISq+GDttK79LFl8EHQkAPvzwctoasC4hs8tBt6kLAncBWWyq1N52qEfKvDw==} + whatwg-url@17.1.2: + resolution: {integrity: sha512-TEZA+Zqxin7Jjsm2cjRohCmen5awh+hT6Zi3VZdqZlNRk7zvOI/9WpBFg/DWlA56bWnzwm6DuB8NS0EsxQH9uQ==} engines: {node: ^22.14.0 || >=24.0.0} whatwg-url@5.0.0: @@ -6457,8 +6564,8 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@3.2.2: - resolution: {integrity: sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==} + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} engines: {node: '>=20.11'} hasBin: true @@ -6603,9 +6710,9 @@ snapshots: '@asamuzakjp/css-color@6.0.7': dependencies: - '@csstools/css-calc': 3.4.1(@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) - '@csstools/css-color-parser': 4.2.4(@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) - '@csstools/css-parser-algorithms': 4.0.1(@csstools/css-tokenizer@4.0.2) + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) '@csstools/css-tokenizer': 4.0.2 lru-cache: 11.5.3 @@ -7374,7 +7481,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260925.1': optional: true - '@cloudflare/workers-types@5.20260926.1': {} + '@cloudflare/workers-types@5.20261003.1': {} '@cspotcode/source-map-support@0.8.1': dependencies: @@ -7382,23 +7489,23 @@ snapshots: '@csstools/color-helpers@6.1.2': {} - '@csstools/css-calc@3.4.1(@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + '@csstools/css-calc@3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': dependencies: - '@csstools/css-parser-algorithms': 4.0.1(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) '@csstools/css-tokenizer': 4.0.2 - '@csstools/css-color-parser@4.2.4(@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + '@csstools/css-color-parser@4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': dependencies: '@csstools/color-helpers': 6.1.2 - '@csstools/css-calc': 3.4.1(@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) - '@csstools/css-parser-algorithms': 4.0.1(@csstools/css-tokenizer@4.0.2) + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) '@csstools/css-tokenizer': 4.0.2 - '@csstools/css-parser-algorithms@4.0.1(@csstools/css-tokenizer@4.0.2)': + '@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2)': dependencies: '@csstools/css-tokenizer': 4.0.2 - '@csstools/css-syntax-patches-for-csstree@1.1.14(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.15(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -7411,49 +7518,49 @@ snapshots: '@discoveryjs/json-ext@1.1.0': {} - '@dprint/android-arm64@0.57.4': + '@dprint/android-arm64@0.59.0': optional: true - '@dprint/android-x64@0.57.4': + '@dprint/android-x64@0.59.0': optional: true - '@dprint/darwin-arm64@0.57.4': + '@dprint/darwin-arm64@0.59.0': optional: true - '@dprint/darwin-x64@0.57.4': + '@dprint/darwin-x64@0.59.0': optional: true - '@dprint/linux-arm64-glibc@0.57.4': + '@dprint/linux-arm64-glibc@0.59.0': optional: true - '@dprint/linux-arm64-musl@0.57.4': + '@dprint/linux-arm64-musl@0.59.0': optional: true - '@dprint/linux-loong64-glibc@0.57.4': + '@dprint/linux-loong64-glibc@0.59.0': optional: true - '@dprint/linux-loong64-musl@0.57.4': + '@dprint/linux-loong64-musl@0.59.0': optional: true - '@dprint/linux-ppc64-glibc@0.57.4': + '@dprint/linux-ppc64-glibc@0.59.0': optional: true - '@dprint/linux-ppc64-musl@0.57.4': + '@dprint/linux-ppc64-musl@0.59.0': optional: true - '@dprint/linux-riscv64-glibc@0.57.4': + '@dprint/linux-riscv64-glibc@0.59.0': optional: true - '@dprint/linux-x64-glibc@0.57.4': + '@dprint/linux-x64-glibc@0.59.0': optional: true - '@dprint/linux-x64-musl@0.57.4': + '@dprint/linux-x64-musl@0.59.0': optional: true - '@dprint/win32-arm64@0.57.4': + '@dprint/win32-arm64@0.59.0': optional: true - '@dprint/win32-x64@0.57.4': + '@dprint/win32-x64@0.59.0': optional: true '@edge-runtime/primitives@6.0.0': @@ -7479,36 +7586,36 @@ snapshots: repeat-string: 1.6.1 strip-color: 0.1.0 - '@effect/tsgo-darwin-arm64@0.46.1': + '@effect/tsgo-darwin-arm64@0.51.0': optional: true - '@effect/tsgo-darwin-x64@0.46.1': + '@effect/tsgo-darwin-x64@0.51.0': optional: true - '@effect/tsgo-linux-arm64@0.46.1': + '@effect/tsgo-linux-arm64@0.51.0': optional: true - '@effect/tsgo-linux-arm@0.46.1': + '@effect/tsgo-linux-arm@0.51.0': optional: true - '@effect/tsgo-linux-x64@0.46.1': + '@effect/tsgo-linux-x64@0.51.0': optional: true - '@effect/tsgo-win32-arm64@0.46.1': + '@effect/tsgo-win32-arm64@0.51.0': optional: true - '@effect/tsgo-win32-x64@0.46.1': + '@effect/tsgo-win32-x64@0.51.0': optional: true - '@effect/tsgo@0.46.1': + '@effect/tsgo@0.51.0': optionalDependencies: - '@effect/tsgo-darwin-arm64': 0.46.1 - '@effect/tsgo-darwin-x64': 0.46.1 - '@effect/tsgo-linux-arm': 0.46.1 - '@effect/tsgo-linux-arm64': 0.46.1 - '@effect/tsgo-linux-x64': 0.46.1 - '@effect/tsgo-win32-arm64': 0.46.1 - '@effect/tsgo-win32-x64': 0.46.1 + '@effect/tsgo-darwin-arm64': 0.51.0 + '@effect/tsgo-darwin-x64': 0.51.0 + '@effect/tsgo-linux-arm': 0.51.0 + '@effect/tsgo-linux-arm64': 0.51.0 + '@effect/tsgo-linux-x64': 0.51.0 + '@effect/tsgo-win32-arm64': 0.51.0 + '@effect/tsgo-win32-x64': 0.51.0 '@effect/wa-sqlite@0.2.1': {} @@ -7597,7 +7704,7 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@exodus/bytes@1.15.1': {} + '@exodus/bytes@1.16.0': {} '@exodus/schemasafe@1.3.0': {} @@ -7761,14 +7868,14 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 26.6.3 + '@types/node': 26.6.4 jest-mock: 29.7.0 '@jest/fake-timers@29.7.0': dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 26.6.3 + '@types/node': 26.6.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -7802,7 +7909,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -8088,64 +8195,66 @@ snapshots: '@oxc-project/types@0.151.0': {} - '@oxlint/binding-android-arm-eabi@1.85.0': + '@oxc-project/types@0.152.0': {} + + '@oxlint/binding-android-arm-eabi@1.86.0': optional: true - '@oxlint/binding-android-arm64@1.85.0': + '@oxlint/binding-android-arm64@1.86.0': optional: true - '@oxlint/binding-darwin-arm64@1.85.0': + '@oxlint/binding-darwin-arm64@1.86.0': optional: true - '@oxlint/binding-darwin-x64@1.85.0': + '@oxlint/binding-darwin-x64@1.86.0': optional: true - '@oxlint/binding-freebsd-x64@1.85.0': + '@oxlint/binding-freebsd-x64@1.86.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.85.0': + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.85.0': + '@oxlint/binding-linux-arm-musleabihf@1.86.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.85.0': + '@oxlint/binding-linux-arm64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.85.0': + '@oxlint/binding-linux-arm64-musl@1.86.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.85.0': + '@oxlint/binding-linux-ppc64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.85.0': + '@oxlint/binding-linux-riscv64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.85.0': + '@oxlint/binding-linux-riscv64-musl@1.86.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.85.0': + '@oxlint/binding-linux-s390x-gnu@1.86.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.85.0': + '@oxlint/binding-linux-x64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-x64-musl@1.85.0': + '@oxlint/binding-linux-x64-musl@1.86.0': optional: true - '@oxlint/binding-openharmony-arm64@1.85.0': + '@oxlint/binding-openharmony-arm64@1.86.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.85.0': + '@oxlint/binding-win32-arm64-msvc@1.86.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.85.0': + '@oxlint/binding-win32-ia32-msvc@1.86.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.85.0': + '@oxlint/binding-win32-x64-msvc@1.86.0': optional: true - '@oxlint/plugins@1.85.0': {} + '@oxlint/plugins@1.86.0': {} '@pkgjs/parseargs@0.11.0': optional: true @@ -8251,27 +8360,25 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 - '@redis/bloom@6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1))': + '@redis/bloom@6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1))': dependencies: - '@redis/client': 6.2.1(@opentelemetry/api@1.9.1) + '@redis/client': 6.3.0(@opentelemetry/api@1.9.1) - '@redis/client@6.2.1(@opentelemetry/api@1.9.1)': - dependencies: - cluster-key-slot: 1.1.2 + '@redis/client@6.3.0(@opentelemetry/api@1.9.1)': optionalDependencies: '@opentelemetry/api': 1.9.1 - '@redis/json@6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1))': + '@redis/json@6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1))': dependencies: - '@redis/client': 6.2.1(@opentelemetry/api@1.9.1) + '@redis/client': 6.3.0(@opentelemetry/api@1.9.1) - '@redis/search@6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1))': + '@redis/search@6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1))': dependencies: - '@redis/client': 6.2.1(@opentelemetry/api@1.9.1) + '@redis/client': 6.3.0(@opentelemetry/api@1.9.1) - '@redis/time-series@6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1))': + '@redis/time-series@6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1))': dependencies: - '@redis/client': 6.2.1(@opentelemetry/api@1.9.1) + '@redis/client': 6.3.0(@opentelemetry/api@1.9.1) '@redocly/ajv@8.11.2': dependencies: @@ -8299,156 +8406,201 @@ snapshots: '@rolldown/binding-android-arm-eabi@1.2.11': optional: true + '@rolldown/binding-android-arm-eabi@1.2.12': + optional: true + '@rolldown/binding-android-arm64@1.2.11': optional: true + '@rolldown/binding-android-arm64@1.2.12': + optional: true + '@rolldown/binding-darwin-arm64@1.2.11': optional: true + '@rolldown/binding-darwin-arm64@1.2.12': + optional: true + '@rolldown/binding-darwin-x64@1.2.11': optional: true + '@rolldown/binding-darwin-x64@1.2.12': + optional: true + '@rolldown/binding-freebsd-x64@1.2.11': optional: true + '@rolldown/binding-freebsd-x64@1.2.12': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.11': optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.12': + optional: true + '@rolldown/binding-linux-arm64-musl@1.2.11': optional: true + '@rolldown/binding-linux-arm64-musl@1.2.12': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.11': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.11': optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.12': + optional: true + '@rolldown/binding-linux-x64-gnu@1.2.11': optional: true + '@rolldown/binding-linux-x64-gnu@1.2.12': + optional: true + '@rolldown/binding-linux-x64-musl@1.2.11': optional: true + '@rolldown/binding-linux-x64-musl@1.2.12': + optional: true + '@rolldown/binding-openharmony-arm64@1.2.11': optional: true + '@rolldown/binding-openharmony-arm64@1.2.12': + optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.11': optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.12': + optional: true + '@rolldown/binding-win32-x64-msvc@1.2.11': optional: true + '@rolldown/binding-win32-x64-msvc@1.2.12': + optional: true + '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-node-resolve@16.0.3(rollup@4.63.5)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.64.0)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.63.5) + '@rollup/pluginutils': 5.4.0(rollup@4.64.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.12 optionalDependencies: - rollup: 4.63.5 + rollup: 4.64.0 - '@rollup/plugin-replace@6.0.3(rollup@4.63.5)': + '@rollup/plugin-replace@6.0.3(rollup@4.64.0)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.63.5) + '@rollup/pluginutils': 5.4.0(rollup@4.64.0) magic-string: 0.30.21 optionalDependencies: - rollup: 4.63.5 + rollup: 4.64.0 - '@rollup/plugin-terser@1.0.0(rollup@4.63.5)': + '@rollup/plugin-terser@1.0.0(rollup@4.64.0)': dependencies: serialize-javascript: 7.1.2 smob: 1.6.2 terser: 5.51.2 optionalDependencies: - rollup: 4.63.5 + rollup: 4.64.0 - '@rollup/pluginutils@5.4.0(rollup@4.63.5)': + '@rollup/pluginutils@5.4.0(rollup@4.64.0)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: - rollup: 4.63.5 + rollup: 4.64.0 - '@rollup/rollup-android-arm-eabi@4.63.5': + '@rollup/rollup-android-arm-eabi@4.64.0': optional: true - '@rollup/rollup-android-arm64@4.63.5': + '@rollup/rollup-android-arm64@4.64.0': optional: true - '@rollup/rollup-darwin-arm64@4.63.5': + '@rollup/rollup-darwin-arm64@4.64.0': optional: true - '@rollup/rollup-darwin-x64@4.63.5': + '@rollup/rollup-darwin-x64@4.64.0': optional: true - '@rollup/rollup-freebsd-arm64@4.63.5': + '@rollup/rollup-freebsd-arm64@4.64.0': optional: true - '@rollup/rollup-freebsd-x64@4.63.5': + '@rollup/rollup-freebsd-x64@4.64.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.63.5': + '@rollup/rollup-linux-arm-gnueabihf@4.64.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.63.5': + '@rollup/rollup-linux-arm-musleabihf@4.64.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.63.5': + '@rollup/rollup-linux-arm64-gnu@4.64.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.63.5': + '@rollup/rollup-linux-arm64-musl@4.64.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.63.5': + '@rollup/rollup-linux-loong64-gnu@4.64.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.63.5': + '@rollup/rollup-linux-loong64-musl@4.64.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.63.5': + '@rollup/rollup-linux-ppc64-gnu@4.64.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.63.5': + '@rollup/rollup-linux-ppc64-musl@4.64.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.63.5': + '@rollup/rollup-linux-riscv64-gnu@4.64.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.63.5': + '@rollup/rollup-linux-riscv64-musl@4.64.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.63.5': + '@rollup/rollup-linux-s390x-gnu@4.64.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.63.5': + '@rollup/rollup-linux-x64-gnu@4.64.0': optional: true - '@rollup/rollup-linux-x64-musl@4.63.5': + '@rollup/rollup-linux-x64-musl@4.64.0': optional: true - '@rollup/rollup-openbsd-x64@4.63.5': + '@rollup/rollup-openbsd-x64@4.64.0': optional: true - '@rollup/rollup-openharmony-arm64@4.63.5': + '@rollup/rollup-openharmony-arm64@4.64.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.63.5': + '@rollup/rollup-win32-arm64-msvc@4.64.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.63.5': + '@rollup/rollup-win32-ia32-msvc@4.64.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.63.5': + '@rollup/rollup-win32-x64-gnu@4.64.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.63.5': + '@rollup/rollup-win32-x64-msvc@4.64.0': optional: true '@sinclair/typebox@0.27.12': {} @@ -8491,36 +8643,36 @@ snapshots: '@sqliteai/sqlite-vector-win32-x86_64@1.0.0': optional: true - '@testcontainers/mssqlserver@12.1.0(supports-color@10.2.2)': + '@testcontainers/mssqlserver@12.2.0(supports-color@10.2.2)': dependencies: - testcontainers: 12.1.0(supports-color@10.2.2) + testcontainers: 12.2.0(supports-color@10.2.2) transitivePeerDependencies: - bare-abort-controller - bare-buffer - react-native-b4a - supports-color - '@testcontainers/mysql@12.1.0(supports-color@10.2.2)': + '@testcontainers/mysql@12.2.0(supports-color@10.2.2)': dependencies: - testcontainers: 12.1.0(supports-color@10.2.2) + testcontainers: 12.2.0(supports-color@10.2.2) transitivePeerDependencies: - bare-abort-controller - bare-buffer - react-native-b4a - supports-color - '@testcontainers/postgresql@12.1.0(supports-color@10.2.2)': + '@testcontainers/postgresql@12.2.0(supports-color@10.2.2)': dependencies: - testcontainers: 12.1.0(supports-color@10.2.2) + testcontainers: 12.2.0(supports-color@10.2.2) transitivePeerDependencies: - bare-abort-controller - bare-buffer - react-native-b4a - supports-color - '@testcontainers/redis@12.1.0(supports-color@10.2.2)': + '@testcontainers/redis@12.2.0(supports-color@10.2.2)': dependencies: - testcontainers: 12.1.0(supports-color@10.2.2) + testcontainers: 12.2.0(supports-color@10.2.2) transitivePeerDependencies: - bare-abort-controller - bare-buffer @@ -8538,7 +8690,7 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.2)': + '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3)': dependencies: '@adobe/css-tools': 4.5.0 '@testing-library/dom': 10.4.2 @@ -8548,7 +8700,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 optionalDependencies: - vitest: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: @@ -8621,13 +8773,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/ssh2': 1.15.6 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/ssh2': 1.15.6 '@types/doctrine@0.0.9': {} @@ -8638,7 +8790,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/istanbul-lib-coverage@2.0.6': {} @@ -8661,13 +8813,13 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@26.6.3': + '@types/node@26.6.4': dependencies: undici-types: 8.9.0 '@types/nodemailer@8.0.2': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/react-dom@19.3.0(@types/react@19.3.0)': dependencies: @@ -8679,7 +8831,7 @@ snapshots: '@types/readable-stream@4.0.24': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/resolve@1.20.2': {} @@ -8687,11 +8839,11 @@ snapshots: '@types/ssh2-streams@0.1.13': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/ssh2@0.5.52': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/ssh2-streams': 0.1.13 '@types/ssh2@1.15.6': @@ -8702,14 +8854,18 @@ snapshots: '@types/swagger2openapi@7.0.4': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 openapi-types: 12.1.3 '@types/whatwg-mimetype@3.0.2': {} '@types/ws@8.18.1': dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 + + '@types/ws@8.18.2': + dependencies: + '@types/node': 26.6.4 '@types/yargs-parser@21.0.3': {} @@ -8820,39 +8976,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@5.0.2(vitest@5.0.2)': + '@vitest/coverage-v8@5.0.3(vitest@5.0.3)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/istanbul-lib-coverage': 1.0.1 - '@vitest/istanbul-lib-report': 1.0.1 + '@vitest/istanbul-lib-coverage': 1.0.2 + '@vitest/istanbul-lib-report': 1.0.2 ast-v8-to-istanbul: 1.0.7 magicast: 0.5.5 obug: 2.2.1 - std-env: 4.2.0 - tinyrainbow: 3.1.1 - vitest: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + std-env: 4.3.0 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) - '@vitest/istanbul-lib-coverage@1.0.1': {} + '@vitest/istanbul-lib-coverage@1.0.2': {} - '@vitest/istanbul-lib-report@1.0.1': + '@vitest/istanbul-lib-report@1.0.2': dependencies: - '@vitest/istanbul-lib-coverage': 1.0.1 + '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@5.0.2(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': + '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': dependencies: '@jridgewell/trace-mapping': 0.3.31 - '@vitest/spy': 5.0.2 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 magic-string: 1.4.2 optionalDependencies: - vite: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - '@vitest/spy@5.0.2': {} + '@vitest/spy@5.0.3': {} - '@vitest/web-worker@5.0.2(vitest@5.0.2)': + '@vitest/web-worker@5.0.3(vitest@5.0.3)': dependencies: obug: 2.2.1 - vitest: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) '@vue/compiler-core@3.5.43': dependencies: @@ -9183,7 +9339,7 @@ snapshots: buffer-image-size@0.6.4: dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 buffer@5.7.1: dependencies: @@ -9200,7 +9356,7 @@ snapshots: bun-types@1.4.2: dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 bundle-name@4.1.1: dependencies: @@ -9218,14 +9374,14 @@ snapshots: caniuse-lite@1.0.30001812: {} - chai@6.2.2: {} + chai@6.3.0: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@6.0.0: {} + chalk@6.0.1: {} change-case@5.4.4: {} @@ -9239,7 +9395,7 @@ snapshots: chrome-launcher@0.15.2(supports-color@10.2.2): dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2(supports-color@10.2.2) @@ -9248,7 +9404,7 @@ snapshots: chromium-edge-launcher@0.2.0(supports-color@10.2.2): dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2(supports-color@10.2.2) @@ -9289,8 +9445,6 @@ snapshots: cluster-key-slot@1.1.0: {} - cluster-key-slot@1.1.2: {} - code-block-writer@13.0.3: {} color-convert@2.0.1: @@ -9371,7 +9525,7 @@ snapshots: css-tree@3.2.1: dependencies: mdn-data: 2.27.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css.escape@1.5.1: {} @@ -9527,23 +9681,23 @@ snapshots: dom-accessibility-api@0.6.3: {} - dprint@0.57.4: + dprint@0.59.0: optionalDependencies: - '@dprint/android-arm64': 0.57.4 - '@dprint/android-x64': 0.57.4 - '@dprint/darwin-arm64': 0.57.4 - '@dprint/darwin-x64': 0.57.4 - '@dprint/linux-arm64-glibc': 0.57.4 - '@dprint/linux-arm64-musl': 0.57.4 - '@dprint/linux-loong64-glibc': 0.57.4 - '@dprint/linux-loong64-musl': 0.57.4 - '@dprint/linux-ppc64-glibc': 0.57.4 - '@dprint/linux-ppc64-musl': 0.57.4 - '@dprint/linux-riscv64-glibc': 0.57.4 - '@dprint/linux-x64-glibc': 0.57.4 - '@dprint/linux-x64-musl': 0.57.4 - '@dprint/win32-arm64': 0.57.4 - '@dprint/win32-x64': 0.57.4 + '@dprint/android-arm64': 0.59.0 + '@dprint/android-x64': 0.59.0 + '@dprint/darwin-arm64': 0.59.0 + '@dprint/darwin-x64': 0.59.0 + '@dprint/linux-arm64-glibc': 0.59.0 + '@dprint/linux-arm64-musl': 0.59.0 + '@dprint/linux-loong64-glibc': 0.59.0 + '@dprint/linux-loong64-musl': 0.59.0 + '@dprint/linux-ppc64-glibc': 0.59.0 + '@dprint/linux-ppc64-musl': 0.59.0 + '@dprint/linux-riscv64-glibc': 0.59.0 + '@dprint/linux-x64-glibc': 0.59.0 + '@dprint/linux-x64-musl': 0.59.0 + '@dprint/win32-arm64': 0.59.0 + '@dprint/win32-x64': 0.59.0 eastasianwidth@0.2.0: {} @@ -9870,7 +10024,7 @@ snapshots: happy-dom@20.14.5: dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 buffer-image-size: 0.6.4 @@ -9901,11 +10055,11 @@ snapshots: dependencies: hermes-estree: 0.35.0 - hono@4.13.9: {} + hono@4.13.12: {} html-encoding-sniffer@6.0.0: dependencies: - '@exodus/bytes': 1.15.1 + '@exodus/bytes': 1.16.0 transitivePeerDependencies: - '@noble/hashes' @@ -9945,7 +10099,7 @@ snapshots: dependencies: queue: 6.0.2 - immer@11.1.18: {} + immer@11.1.21: {} import-meta-resolve@4.2.0: {} @@ -10063,7 +10217,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 26.6.3 + '@types/node': 26.6.4 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -10073,7 +10227,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 26.6.3 + '@types/node': 26.6.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -10100,7 +10254,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 26.6.3 + '@types/node': 26.6.4 jest-util: 29.7.0 jest-regex-util@29.6.3: {} @@ -10108,7 +10262,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 26.6.3 + '@types/node': 26.6.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -10125,7 +10279,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -10181,8 +10335,8 @@ snapshots: '@asamuzakjp/css-color': 6.0.7 '@asamuzakjp/dom-selector': 8.3.2 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.14(css-tree@3.2.1) - '@exodus/bytes': 1.15.1 + '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) + '@exodus/bytes': 1.16.0 css-tree: 3.2.1 data-urls: 7.0.0 decimal.js: 10.6.0 @@ -10197,7 +10351,7 @@ snapshots: w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 17.1.1 + whatwg-url: 17.1.2 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -10447,7 +10601,7 @@ snapshots: dependencies: '@babel/parser': 7.29.9 '@babel/types': 7.29.8 - source-map-js: 1.2.1 + source-map-js: 1.2.2 make-dir@2.1.0: dependencies: @@ -10665,10 +10819,10 @@ snapshots: min-indent@1.0.1: {} - miniflare@5.20260925.0-alpha(@types/node@26.6.3): + miniflare@5.20260925.0-alpha(@types/node@26.6.4): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.4(@types/node@26.6.3) + sharp: 0.35.4(@types/node@26.6.4) undici: 7.29.0 workerd: 1.20260925.1 ws: 8.21.0 @@ -10732,9 +10886,9 @@ snapshots: ms@2.1.3: {} - mysql2@3.24.4(@types/node@26.6.3): + mysql2@3.24.5(@types/node@26.6.4): dependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 aws-ssl-profiles: 1.1.2 generate-function: 2.3.1 iconv-lite: 0.7.3 @@ -10782,7 +10936,7 @@ snapshots: dependencies: '@babel/parser': 7.29.9 - nodemailer@10.0.10: {} + nodemailer@10.0.14: {} normalize-path@3.0.0: {} @@ -10890,27 +11044,27 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - oxlint@1.85.0: + oxlint@1.86.0: optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.85.0 - '@oxlint/binding-android-arm64': 1.85.0 - '@oxlint/binding-darwin-arm64': 1.85.0 - '@oxlint/binding-darwin-x64': 1.85.0 - '@oxlint/binding-freebsd-x64': 1.85.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.85.0 - '@oxlint/binding-linux-arm-musleabihf': 1.85.0 - '@oxlint/binding-linux-arm64-gnu': 1.85.0 - '@oxlint/binding-linux-arm64-musl': 1.85.0 - '@oxlint/binding-linux-ppc64-gnu': 1.85.0 - '@oxlint/binding-linux-riscv64-gnu': 1.85.0 - '@oxlint/binding-linux-riscv64-musl': 1.85.0 - '@oxlint/binding-linux-s390x-gnu': 1.85.0 - '@oxlint/binding-linux-x64-gnu': 1.85.0 - '@oxlint/binding-linux-x64-musl': 1.85.0 - '@oxlint/binding-openharmony-arm64': 1.85.0 - '@oxlint/binding-win32-arm64-msvc': 1.85.0 - '@oxlint/binding-win32-ia32-msvc': 1.85.0 - '@oxlint/binding-win32-x64-msvc': 1.85.0 + '@oxlint/binding-android-arm-eabi': 1.86.0 + '@oxlint/binding-android-arm64': 1.86.0 + '@oxlint/binding-darwin-arm64': 1.86.0 + '@oxlint/binding-darwin-x64': 1.86.0 + '@oxlint/binding-freebsd-x64': 1.86.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.86.0 + '@oxlint/binding-linux-arm-musleabihf': 1.86.0 + '@oxlint/binding-linux-arm64-gnu': 1.86.0 + '@oxlint/binding-linux-arm64-musl': 1.86.0 + '@oxlint/binding-linux-ppc64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-musl': 1.86.0 + '@oxlint/binding-linux-s390x-gnu': 1.86.0 + '@oxlint/binding-linux-x64-gnu': 1.86.0 + '@oxlint/binding-linux-x64-musl': 1.86.0 + '@oxlint/binding-openharmony-arm64': 1.86.0 + '@oxlint/binding-win32-arm64-msvc': 1.86.0 + '@oxlint/binding-win32-ia32-msvc': 1.86.0 + '@oxlint/binding-win32-x64-msvc': 1.86.0 p-limit@2.3.0: dependencies: @@ -11087,7 +11241,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.6.3 + '@types/node': 26.6.4 long: 5.3.2 protobufjs@8.8.0: @@ -11242,13 +11396,13 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - redis@6.2.1(@opentelemetry/api@1.9.1): + redis@6.3.0(@opentelemetry/api@1.9.1): dependencies: - '@redis/bloom': 6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1)) - '@redis/client': 6.2.1(@opentelemetry/api@1.9.1) - '@redis/json': 6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1)) - '@redis/search': 6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1)) - '@redis/time-series': 6.2.1(@redis/client@6.2.1(@opentelemetry/api@1.9.1)) + '@redis/bloom': 6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1)) + '@redis/client': 6.3.0(@opentelemetry/api@1.9.1) + '@redis/json': 6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1)) + '@redis/search': 6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1)) + '@redis/time-series': 6.3.0(@redis/client@6.3.0(@opentelemetry/api@1.9.1)) transitivePeerDependencies: - '@node-rs/xxhash' - '@opentelemetry/api' @@ -11322,84 +11476,105 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.11 '@rolldown/binding-win32-x64-msvc': 1.2.11 - rollup-plugin-bundle-stats@4.22.3(core-js@3.47.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): + rolldown@1.2.12: + dependencies: + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 + + rollup-plugin-bundle-stats@4.22.3(core-js@3.47.0)(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@bundle-stats/cli-utils': 4.22.3(core-js@3.47.0) - rollup-plugin-stats: 2.1.3(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) - rollup-plugin-webpack-stats: 3.1.3(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + rollup-plugin-stats: 2.1.3(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + rollup-plugin-webpack-stats: 3.1.3(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) tslib: 2.8.1 optionalDependencies: - rolldown: 1.2.11 - rollup: 4.63.5 - vite: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + rolldown: 1.2.12 + rollup: 4.64.0 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) transitivePeerDependencies: - core-js - rollup-plugin-esbuild@6.2.1(esbuild@0.28.2)(rollup@4.63.5)(supports-color@10.2.2): + rollup-plugin-esbuild@6.2.1(esbuild@0.28.2)(rollup@4.64.0)(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 esbuild: 0.28.2 get-tsconfig: 4.14.3 - rollup: 4.63.5 + rollup: 4.64.0 unplugin-utils: 0.2.5 transitivePeerDependencies: - supports-color - rollup-plugin-stats@2.1.3(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): + rollup-plugin-stats@2.1.3(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): optionalDependencies: - rolldown: 1.2.11 - rollup: 4.63.5 - vite: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + rolldown: 1.2.12 + rollup: 4.64.0 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5): + rollup-plugin-visualizer@7.1.1(rolldown@1.2.12)(rollup@4.64.0): dependencies: open: 11.0.4 picomatch: 4.0.7 source-map: 0.8.0 yargs: 18.2.0 optionalDependencies: - rolldown: 1.2.11 - rollup: 4.63.5 + rolldown: 1.2.12 + rollup: 4.64.0 - rollup-plugin-webpack-stats@3.1.3(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): + rollup-plugin-webpack-stats@3.1.3(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: - rollup-plugin-stats: 2.1.3(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + rollup-plugin-stats: 2.1.3(rolldown@1.2.12)(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) optionalDependencies: - rolldown: 1.2.11 - rollup: 4.63.5 - vite: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + rolldown: 1.2.12 + rollup: 4.64.0 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - rollup@4.63.5: + rollup@4.64.0: dependencies: '@types/estree': 1.0.9 optionalDependencies: '@napi-rs/lzma-linux-x64-gnu': 1.5.1 - '@rollup/rollup-android-arm-eabi': 4.63.5 - '@rollup/rollup-android-arm64': 4.63.5 - '@rollup/rollup-darwin-arm64': 4.63.5 - '@rollup/rollup-darwin-x64': 4.63.5 - '@rollup/rollup-freebsd-arm64': 4.63.5 - '@rollup/rollup-freebsd-x64': 4.63.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.63.5 - '@rollup/rollup-linux-arm-musleabihf': 4.63.5 - '@rollup/rollup-linux-arm64-gnu': 4.63.5 - '@rollup/rollup-linux-arm64-musl': 4.63.5 - '@rollup/rollup-linux-loong64-gnu': 4.63.5 - '@rollup/rollup-linux-loong64-musl': 4.63.5 - '@rollup/rollup-linux-ppc64-gnu': 4.63.5 - '@rollup/rollup-linux-ppc64-musl': 4.63.5 - '@rollup/rollup-linux-riscv64-gnu': 4.63.5 - '@rollup/rollup-linux-riscv64-musl': 4.63.5 - '@rollup/rollup-linux-s390x-gnu': 4.63.5 - '@rollup/rollup-linux-x64-gnu': 4.63.5 - '@rollup/rollup-linux-x64-musl': 4.63.5 - '@rollup/rollup-openbsd-x64': 4.63.5 - '@rollup/rollup-openharmony-arm64': 4.63.5 - '@rollup/rollup-win32-arm64-msvc': 4.63.5 - '@rollup/rollup-win32-ia32-msvc': 4.63.5 - '@rollup/rollup-win32-x64-gnu': 4.63.5 - '@rollup/rollup-win32-x64-msvc': 4.63.5 + '@rollup/rollup-android-arm-eabi': 4.64.0 + '@rollup/rollup-android-arm64': 4.64.0 + '@rollup/rollup-darwin-arm64': 4.64.0 + '@rollup/rollup-darwin-x64': 4.64.0 + '@rollup/rollup-freebsd-arm64': 4.64.0 + '@rollup/rollup-freebsd-x64': 4.64.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.64.0 + '@rollup/rollup-linux-arm-musleabihf': 4.64.0 + '@rollup/rollup-linux-arm64-gnu': 4.64.0 + '@rollup/rollup-linux-arm64-musl': 4.64.0 + '@rollup/rollup-linux-loong64-gnu': 4.64.0 + '@rollup/rollup-linux-loong64-musl': 4.64.0 + '@rollup/rollup-linux-ppc64-gnu': 4.64.0 + '@rollup/rollup-linux-ppc64-musl': 4.64.0 + '@rollup/rollup-linux-riscv64-gnu': 4.64.0 + '@rollup/rollup-linux-riscv64-musl': 4.64.0 + '@rollup/rollup-linux-s390x-gnu': 4.64.0 + '@rollup/rollup-linux-x64-gnu': 4.64.0 + '@rollup/rollup-linux-x64-musl': 4.64.0 + '@rollup/rollup-openbsd-x64': 4.64.0 + '@rollup/rollup-openharmony-arm64': 4.64.0 + '@rollup/rollup-win32-arm64-msvc': 4.64.0 + '@rollup/rollup-win32-ia32-msvc': 4.64.0 + '@rollup/rollup-win32-x64-gnu': 4.64.0 + '@rollup/rollup-win32-x64-msvc': 4.64.0 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -11478,7 +11653,7 @@ snapshots: dependencies: kind-of: 6.0.3 - sharp@0.35.4(@types/node@26.6.3): + sharp@0.35.4(@types/node@26.6.4): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -11509,7 +11684,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.4 '@img/sharp-win32-ia32': 0.35.4 '@img/sharp-win32-x64': 0.35.4 - '@types/node': 26.6.3 + '@types/node': 26.6.4 shebang-command@2.0.0: dependencies: @@ -11565,6 +11740,8 @@ snapshots: source-map-js@1.2.1: {} + source-map-js@1.2.2: {} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -11611,7 +11788,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.2.0: {} + std-env@4.3.0: {} stream-chain@2.2.5: {} @@ -11775,13 +11952,13 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - tedious@20.0.0(supports-color@10.2.2): + tedious@20.3.3(supports-color@10.2.2): dependencies: '@azure/core-auth': 1.11.0(supports-color@10.2.2) '@azure/identity': 4.13.3(supports-color@10.2.2) '@azure/keyvault-keys': 4.10.2(supports-color@10.2.2) '@js-joda/core': 6.1.0 - '@types/node': 26.6.3 + '@types/node': 26.6.4 bl: 6.1.6 iconv-lite: 0.7.3 js-md4: 0.3.2 @@ -11810,7 +11987,7 @@ snapshots: glob: 7.2.3 minimatch: 3.1.5 - testcontainers@12.1.0(supports-color@10.2.2): + testcontainers@12.2.0(supports-color@10.2.2): dependencies: '@balena/dockerignore': 1.0.2 '@types/dockerode': 4.0.1 @@ -11857,7 +12034,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - tinyrainbow@3.1.1: {} + tinyrainbow@3.2.0: {} tldts-core@7.4.9: {} @@ -12017,7 +12194,7 @@ snapshots: verkit@0.3.2: {} - vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1): + vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -12025,39 +12202,39 @@ snapshots: rolldown: 1.2.11 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.6.3 + '@types/node': 26.6.4 esbuild: 0.28.2 fsevents: 2.3.3 terser: 5.51.2 tsx: 4.23.15 yaml: 2.9.1 - vitest-websocket-mock@0.8.0(vitest@5.0.2): + vitest-websocket-mock@0.8.0(vitest@5.0.3): dependencies: mock-socket: 9.3.1 - vitest: 5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) - vitest@5.0.2(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@5.0.2)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): + vitest@5.0.3(@edge-runtime/vm@5.0.0)(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.2(vite@8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) - chai: 6.2.2 + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 magic-string: 1.4.2 obug: 2.2.1 picomatch: 4.0.7 - std-env: 4.2.0 + std-env: 4.3.0 tinybench: 6.2.0 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 8.3.1(@types/node@26.6.3)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - why-is-node-running: 3.2.2 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + why-is-node-running: 3.2.1 optionalDependencies: '@edge-runtime/vm': 5.0.0 '@opentelemetry/api': 1.9.1 - '@types/node': 26.6.3 - '@vitest/coverage-v8': 5.0.2(vitest@5.0.2) + '@types/node': 26.6.4 + '@vitest/coverage-v8': 5.0.3(vitest@5.0.3) happy-dom: 20.14.5 jsdom: 30.0.1 transitivePeerDependencies: @@ -12101,15 +12278,15 @@ snapshots: whatwg-url@16.0.1: dependencies: - '@exodus/bytes': 1.15.1 + '@exodus/bytes': 1.16.0 tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - whatwg-url@17.1.1: + whatwg-url@17.1.2: dependencies: - '@exodus/bytes': 1.15.1 + '@exodus/bytes': 1.16.0 tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: @@ -12124,7 +12301,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@3.2.2: {} + why-is-node-running@3.2.1: {} workerd@1.20260925.1: optionalDependencies: diff --git a/repos/effect/pnpm-workspace.yaml b/repos/effect/pnpm-workspace.yaml index 80c11ab74..2919210a4 100644 --- a/repos/effect/pnpm-workspace.yaml +++ b/repos/effect/pnpm-workspace.yaml @@ -34,3 +34,13 @@ overrides: # Remove after a later jsdom release passes the Atom React and Solid suites under Bun. # Upstream changes: https://github.com/jsdom/jsdom/compare/v30.0.1...v30.1.0 jsdom: ~30.0.1 + +minimumReleaseAgeExclude: + - '@effect/tsgo-darwin-arm64@0.51.0' + - '@effect/tsgo-darwin-x64@0.51.0' + - '@effect/tsgo-linux-arm64@0.51.0' + - '@effect/tsgo-linux-arm@0.51.0' + - '@effect/tsgo-linux-x64@0.51.0' + - '@effect/tsgo-win32-arm64@0.51.0' + - '@effect/tsgo-win32-x64@0.51.0' + - '@effect/tsgo@0.51.0' diff --git a/repos/effect/scripts/effect-bot.mjs b/repos/effect/scripts/effect-bot.mjs new file mode 100644 index 000000000..25db29d39 --- /dev/null +++ b/repos/effect/scripts/effect-bot.mjs @@ -0,0 +1,116 @@ +import { createHmac } from "node:crypto" +import { readFile } from "node:fs/promises" +import { pathToFileURL } from "node:url" + +export const isCommand = (body) => /^\/effect-bot(?:\s|$)/u.test(body) +export const deliveryKey = (eventName, event) => + `${event.repository.full_name}:${eventName}:${event.comment.id}` + +export async function relayComment(options) { + const { eventName, event, githubToken, webhookUrl, webhookSecret } = options + const fetch = options.fetch ?? globalThis.fetch + const matches = options.isCommand ?? isCommand + const keyFor = options.deliveryKey ?? deliveryKey + if (eventName !== "issue_comment" || + event?.action !== "created" || typeof event.comment?.body !== "string" || + !matches(event.comment.body)) return { status: "ignored" } + + const repo = event.repository?.full_name + const number = event.issue?.number + const comment = event.comment + const author = comment.user?.login + if (typeof repo !== "string" || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(repo) || + typeof author !== "string" || !/^[A-Za-z0-9-]+(?:\[bot\])?$/u.test(author) || + !Number.isSafeInteger(number) || number <= 0 || + !Number.isSafeInteger(comment.id) || comment.id <= 0 || + typeof comment.html_url !== "string") throw new Error("Malformed comment event") + if (!githubToken || !webhookUrl || typeof webhookSecret !== "string" || !webhookSecret) { + throw new Error("Missing relay configuration") + } + const endpoint = new URL(webhookUrl) + if (endpoint.protocol !== "https:" || endpoint.username || endpoint.password) { + throw new Error("Webhook must be an HTTPS URL without credentials") + } + const githubHeaders = { + Authorization: `Bearer ${githubToken}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "Content-Type": "application/json" + } + const request = (url, init) => fetch(url, { + ...init, + redirect: "error", + signal: AbortSignal.timeout(30_000) + }) + const githubBase = `https://api.github.com/repos/${repo}` + const permission = await request(`${githubBase}/collaborators/${encodeURIComponent(author)}/permission`, { + headers: githubHeaders + }) + if (!permission.ok) throw new Error(`Permission lookup failed: HTTP ${permission.status}`) + const { role_name: role } = await permission.json() + // No author_association, permission fallback, or custom-role inference. + if (role !== "admin" && role !== "maintain") return { status: "rejected" } + + const payload = { + action: "created", + repo, + number, + isPullRequest: Boolean(event.issue?.pull_request), + commentId: comment.id, + commentUrl: comment.html_url, + body: comment.body, + author + } + const body = JSON.stringify(payload) + const signature = `sha256=${createHmac("sha256", webhookSecret).update(body, "utf8").digest("hex")}` + const key = keyFor(eventName, event) + if (typeof key !== "string" || !key || /[\r\n]/u.test(key)) throw new Error("Invalid delivery key") + const response = await request(webhookUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-GitHub-Event": eventName, + "X-GitHub-Delivery": key, + "X-Hub-Signature-256": signature + }, + body + }) + if (!response.ok) throw new Error(`Webhook failed: HTTP ${response.status}`) + const result = await response.json() + if (!["accepted", "duplicate", "ignored", "skipped"].includes(result?.status)) { + throw new Error("Unrecognized webhook admission response") + } + if ((result.status === "accepted" || result.status === "duplicate") && + typeof result.run_id === "string" && result.run_id.length > 0) { + try { + const reaction = await request(`${githubBase}/issues/comments/${comment.id}/reactions`, { + method: "POST", headers: githubHeaders, body: JSON.stringify({ content: "eyes" }) + }) + if (!reaction.ok) throw new Error("Reaction HTTP error") + } catch { + // An admitted request must never be redelivered merely to retry feedback. + return { ...result, reactionFailed: true } + } + } + return result +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + try { + const event = JSON.parse(await readFile(process.env.GITHUB_EVENT_PATH, "utf8")) + // Fixed repository boundary; no event-supplied API hosts or executable text. + if (event.repository?.full_name !== "Effect-TS/effect") throw new Error("Unexpected repository") + const result = await relayComment({ + eventName: process.env.GITHUB_EVENT_NAME, event, + githubToken: process.env.GITHUB_TOKEN, + webhookUrl: process.env.MULTICA_EFFECT_BOT_WEBHOOK_URL, + webhookSecret: process.env.MULTICA_EFFECT_BOT_WEBHOOK_SECRET + }) + // Never log the body, credentials, signature, endpoint, or raw remote errors. + console.log(`Effect bot relay: ${result.status}${result.reactionFailed ? " (reaction failed)" : ""}`) + if (["ignored", "skipped"].includes(result.status)) process.exitCode = 1 + } catch { + console.error("Effect bot relay failed; check configuration and API permissions. No automatic retry.") + process.exitCode = 1 + } +} diff --git a/repos/effect/scripts/version.mjs b/repos/effect/scripts/version.mjs new file mode 100644 index 000000000..83a2649ac --- /dev/null +++ b/repos/effect/scripts/version.mjs @@ -0,0 +1,8 @@ +import * as Fs from "node:fs" + +const { version } = JSON.parse(Fs.readFileSync("packages/effect/package.json", "utf8")) + +Fs.writeFileSync( + "packages/effect/src/internal/version.ts", + `/** @internal */\nexport const version = ${JSON.stringify(version)}\n` +) diff --git a/repos/effect/tsconfig.base.json b/repos/effect/tsconfig.base.json index 0a0a036e2..deb466d3a 100644 --- a/repos/effect/tsconfig.base.json +++ b/repos/effect/tsconfig.base.json @@ -35,11 +35,32 @@ "name": "@effect/language-service", "namespaceImportPackages": ["effect", "@effect/*"], "diagnosticSeverity": { - "globalErrorInEffectFailure": "off" // TODO: Check if we should fix the warnings this reports + "globalErrorInEffectFailure": "off", // TODO: Check if we should fix the warnings this reports + "unstableApiUsage": "off" }, "includeSuggestionsInTsc": false, "ignoreEffectWarningsInTscExitCode": true, - "ignoreEffectErrorsInTscExitCode": true + "ignoreEffectErrorsInTscExitCode": true, + "overrides": [ + { + "exclude": [ + "**/internal/**/*", + "**/index.ts", + "**/index.tsx", + "**/index.mts", + "**/index.cts", + "**/ai-docs/**/*", + "**/test/**/*", + "**/Generated.ts", + "**/packages/tools/**/*" + ], + "options": { + "diagnosticSeverity": { + "apiStabilityLeak": "error" + } + } + } + ] }] } } diff --git a/repos/effect/tsconfig.tests.json b/repos/effect/tsconfig.tests.json index 1224e3355..4b7207369 100644 --- a/repos/effect/tsconfig.tests.json +++ b/repos/effect/tsconfig.tests.json @@ -118,7 +118,8 @@ ], "diagnosticSeverity": { "unknownInEffectCatch": "off", - "multipleEffectProvide": "off" + "multipleEffectProvide": "off", + "unstableApiUsage": "off" } }] }